AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/09 09:54 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "VXLAN",
    "MLXSW_SPECTRUM",
    "OPENVSWITCH_VXLAN"
  ],
  "FocusSymbols": [
    "vxlan_changelink",
    "vxlan_dev_configure",
    "vxlan_fdb_create",
    "vxlan_nl2conf",
    "vxlan_fill_info",
    "vxlan_mdb_config_init",
    "vxlan_vni_add",
    "vxlan_vnifilter_process",
    "vxlan_get_options",
    "mlxsw_sp_nve_vxlan_config"
  ],
  "Reasoning": "The patch changes how the VXLAN configuration (`vxlan-\u003ecfg`) is accessed, moving it from an embedded struct to an RCU-protected pointer. This is a significant functional change in the core VXLAN driver, affecting device creation, configuration, FDB, MDB, VNI filtering, and integration with other subsystems like Open vSwitch and Mellanox Spectrum switchdev. It introduces new memory allocations and RCU dereferences, which should be fuzzed to ensure no use-after-free or memory leaks occur.",
  "WorthFuzzing": true
}

1/1 2026/07/09 09:54 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 0975054fe9075c08bc58349b1bd8d7c10d8bc26d\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Thu Jul 9 09:54:56 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c\nindex 52c2fe3644d4b9..50cea39323f570 100644\n--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c\n+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c\n@@ -59,8 +59,11 @@ static bool mlxsw_sp_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,\n \t\t\t\t\t   const struct mlxsw_sp_nve_params *params,\n \t\t\t\t\t   struct netlink_ext_ack *extack)\n {\n-\tstruct vxlan_dev *vxlan = netdev_priv(params-\u003edev);\n-\tstruct vxlan_config *cfg = \u0026vxlan-\u003ecfg;\n+\tconst struct vxlan_config *cfg;\n+\tstruct vxlan_dev *vxlan;\n+\n+\tvxlan = netdev_priv(params-\u003edev);\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n \n \tif (vxlan_addr_multicast(\u0026cfg-\u003eremote_ip)) {\n \t\tNL_SET_ERR_MSG_MOD(extack, \"VxLAN: Multicast destination IP is not supported\");\n@@ -148,8 +151,11 @@ static void mlxsw_sp_nve_vxlan_config(const struct mlxsw_sp_nve *nve,\n \t\t\t\t      const struct mlxsw_sp_nve_params *params,\n \t\t\t\t      struct mlxsw_sp_nve_config *config)\n {\n-\tstruct vxlan_dev *vxlan = netdev_priv(params-\u003edev);\n-\tstruct vxlan_config *cfg = \u0026vxlan-\u003ecfg;\n+\tconst struct vxlan_config *cfg;\n+\tstruct vxlan_dev *vxlan;\n+\n+\tvxlan = netdev_priv(params-\u003edev);\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n \n \tconfig-\u003etype = MLXSW_SP_NVE_TYPE_VXLAN;\n \tconfig-\u003ettl = cfg-\u003ettl;\ndiff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c\nindex fe45e533a4b2ef..f25105ceb3f1bf 100644\n--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c\n+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c\n@@ -2513,15 +2513,17 @@ mlxsw_sp_bridge_vlan_aware_vxlan_join(struct mlxsw_sp_bridge_device *bridge_devi\n {\n \tstruct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device-\u003edev);\n \tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\n-\tstruct mlxsw_sp_nve_params params = {\n-\t\t.type = MLXSW_SP_NVE_TYPE_VXLAN,\n-\t\t.vni = vxlan-\u003ecfg.vni,\n-\t\t.dev = vxlan_dev,\n-\t\t.ethertype = ethertype,\n-\t};\n+\tstruct mlxsw_sp_nve_params params;\n+\tconst struct vxlan_config *cfg;\n \tstruct mlxsw_sp_fid *fid;\n \tint err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tparams.type = MLXSW_SP_NVE_TYPE_VXLAN;\n+\tparams.vni = cfg-\u003evni;\n+\tparams.dev = vxlan_dev;\n+\tparams.ethertype = ethertype;\n+\n \t/* If the VLAN is 0, we need to find the VLAN that is configured as\n \t * PVID and egress untagged on the bridge port of the VxLAN device.\n \t * It is possible no such VLAN exists\n@@ -2704,15 +2706,17 @@ mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,\n {\n \tstruct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device-\u003edev);\n \tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\n-\tstruct mlxsw_sp_nve_params params = {\n-\t\t.type = MLXSW_SP_NVE_TYPE_VXLAN,\n-\t\t.vni = vxlan-\u003ecfg.vni,\n-\t\t.dev = vxlan_dev,\n-\t\t.ethertype = ETH_P_8021Q,\n-\t};\n+\tstruct mlxsw_sp_nve_params params;\n+\tconst struct vxlan_config *cfg;\n \tstruct mlxsw_sp_fid *fid;\n \tint err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tparams.type = MLXSW_SP_NVE_TYPE_VXLAN;\n+\tparams.vni = cfg-\u003evni;\n+\tparams.dev = vxlan_dev;\n+\tparams.ethertype = ETH_P_8021Q;\n+\n \tfid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device-\u003edev-\u003eifindex);\n \tif (IS_ERR(fid)) {\n \t\tNL_SET_ERR_MSG_MOD(extack, \"Failed to create 802.1D FID\");\n@@ -2933,10 +2937,13 @@ static void __mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,\n \t\t\t\t\t  const struct net_device *vxlan_dev)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\n+\tconst struct vxlan_config *cfg;\n \tstruct mlxsw_sp_fid *fid;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n \t/* If the VxLAN device is down, then the FID does not have a VNI */\n-\tfid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan-\u003ecfg.vni);\n+\tfid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, cfg-\u003evni);\n \tif (!fid)\n \t\treturn;\n \n@@ -3029,11 +3036,13 @@ static void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev,\n \tstruct switchdev_notifier_vxlan_fdb_info info;\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tenum switchdev_notifier_type type;\n+\tconst struct vxlan_config *cfg;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n \ttype = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE :\n \t\t\tSWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE;\n \tmlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, \u0026info.remote_ip);\n-\tinfo.remote_port = vxlan-\u003ecfg.dst_port;\n+\tinfo.remote_port = cfg-\u003edst_port;\n \tinfo.remote_vni = vni;\n \tinfo.remote_ifindex = 0;\n \tether_addr_copy(info.eth_addr, mac);\n@@ -3236,8 +3245,10 @@ __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,\n \n \tif (adding \u0026\u0026 netif_is_vxlan(dev)) {\n \t\tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\t\tconst struct vxlan_config *cfg;\n \n-\t\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_LEARN))\n+\t\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\t\tif (!(cfg-\u003eflags \u0026 VXLAN_F_LEARN))\n \t\t\treturn -EINVAL;\n \t}\n \n@@ -3722,9 +3733,11 @@ mlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work *\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(switchdev_work-\u003edev);\n \tstruct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;\n-\tstruct vxlan_config *cfg = \u0026vxlan-\u003ecfg;\n+\tconst struct vxlan_config *cfg;\n \tstruct netlink_ext_ack *extack;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n \textack = switchdev_notifier_info_to_extack(info);\n \tvxlan_fdb_info = container_of(info,\n \t\t\t\t      struct switchdev_notifier_vxlan_fdb_info,\n@@ -3851,11 +3864,15 @@ mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,\n \t\t\t\t  struct netlink_ext_ack *extack)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\n-\t__be32 vni = vxlan-\u003ecfg.vni;\n+\tconst struct vxlan_config *cfg;\n \tstruct mlxsw_sp_fid *fid;\n \tu16 old_vid;\n+\t__be32 vni;\n \tint err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tvni = cfg-\u003evni;\n+\n \t/* We cannot have the same VLAN as PVID and egress untagged on multiple\n \t * VxLAN devices. Note that we get this notification before the VLAN is\n \t * actually added to the bridge's database, so it is not possible for\n@@ -3935,12 +3952,16 @@ mlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp,\n \t\t\t\t  const struct net_device *vxlan_dev, u16 vid)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\n-\t__be32 vni = vxlan-\u003ecfg.vni;\n+\tconst struct vxlan_config *cfg;\n \tstruct mlxsw_sp_fid *fid;\n+\t__be32 vni;\n \n \tif (!netif_running(vxlan_dev))\n \t\treturn;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tvni = cfg-\u003evni;\n+\n \tfid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);\n \tif (!fid)\n \t\treturn;\ndiff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c\nindex 67c367cc566233..86b853d7739639 100644\n--- a/drivers/net/vxlan/vxlan_core.c\n+++ b/drivers/net/vxlan/vxlan_core.c\n@@ -110,20 +110,23 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs,\n \t\tvni = 0;\n \n \thlist_for_each_entry_rcu(node, vni_head(vs, vni), hlist) {\n+\t\tconst struct vxlan_config *cfg;\n+\n \t\tif (!node-\u003evxlan)\n \t\t\tcontinue;\n+\n+\t\tcfg = rcu_dereference(node-\u003evxlan-\u003ecfg);\n+\n \t\tvnode = NULL;\n-\t\tif (node-\u003evxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER) {\n+\t\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER) {\n \t\t\tvnode = vxlan_vnifilter_lookup(node-\u003evxlan, vni);\n \t\t\tif (!vnode)\n \t\t\t\tcontinue;\n-\t\t} else if (node-\u003evxlan-\u003edefault_dst.remote_vni != vni) {\n+\t\t} else if (cfg-\u003evni != vni) {\n \t\t\tcontinue;\n \t\t}\n \n \t\tif (IS_ENABLED(CONFIG_IPV6)) {\n-\t\t\tconst struct vxlan_config *cfg = \u0026node-\u003evxlan-\u003ecfg;\n-\n \t\t\tif ((cfg-\u003eflags \u0026 VXLAN_F_IPV6_LINKLOCAL) \u0026\u0026\n \t\t\t    cfg-\u003eremote_ifindex != ifindex)\n \t\t\t\tcontinue;\n@@ -157,6 +160,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,\n \t\t\t  u32 portid, u32 seq, int type, unsigned int flags,\n \t\t\t  const struct vxlan_rdst *rdst)\n {\n+\tconst struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan-\u003ecfg);\n \tunsigned long now = jiffies;\n \tstruct nda_cacheinfo ci;\n \tbool send_ip, send_eth;\n@@ -216,10 +220,10 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,\n \t\t\tgoto nla_put_failure;\n \n \t\tif (rdst-\u003eremote_port \u0026\u0026\n-\t\t    rdst-\u003eremote_port != vxlan-\u003ecfg.dst_port \u0026\u0026\n+\t\t    rdst-\u003eremote_port != cfg-\u003edst_port \u0026\u0026\n \t\t    nla_put_be16(skb, NDA_PORT, rdst-\u003eremote_port))\n \t\t\tgoto nla_put_failure;\n-\t\tif (rdst-\u003eremote_vni != vxlan-\u003edefault_dst.remote_vni \u0026\u0026\n+\t\tif (rdst-\u003eremote_vni != cfg-\u003evni \u0026\u0026\n \t\t    nla_put_u32(skb, NDA_VNI, be32_to_cpu(rdst-\u003eremote_vni)))\n \t\t\tgoto nla_put_failure;\n \t\tif (rdst-\u003eremote_ifindex \u0026\u0026\n@@ -227,7 +231,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,\n \t\t\tgoto nla_put_failure;\n \t}\n \n-\tif ((vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026 fdb-\u003ekey.vni \u0026\u0026\n+\tif ((cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026 fdb-\u003ekey.vni \u0026\u0026\n \t    nla_put_u32(skb, NDA_SRC_VNI,\n \t\t\tbe32_to_cpu(fdb-\u003ekey.vni)))\n \t\tgoto nla_put_failure;\n@@ -377,14 +381,15 @@ static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])\n \n /* Look up Ethernet address in forwarding table */\n static struct vxlan_fdb *vxlan_find_mac_rcu(struct vxlan_dev *vxlan,\n+\t\t\t\t\t    const struct vxlan_config *cfg,\n \t\t\t\t\t    const u8 *mac, __be32 vni)\n {\n \tstruct vxlan_fdb_key key;\n \n \tmemset(\u0026key, 0, sizeof(key));\n \tmemcpy(key.eth_addr, mac, sizeof(key.eth_addr));\n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA))\n-\t\tkey.vni = vxlan-\u003edefault_dst.remote_vni;\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA))\n+\t\tkey.vni = cfg-\u003evni;\n \telse\n \t\tkey.vni = vni;\n \n@@ -393,11 +398,12 @@ static struct vxlan_fdb *vxlan_find_mac_rcu(struct vxlan_dev *vxlan,\n }\n \n static struct vxlan_fdb *vxlan_find_mac_tx(struct vxlan_dev *vxlan,\n+\t\t\t\t\t   const struct vxlan_config *cfg,\n \t\t\t\t\t   const u8 *mac, __be32 vni)\n {\n \tstruct vxlan_fdb *f;\n \n-\tf = vxlan_find_mac_rcu(vxlan, mac, vni);\n+\tf = vxlan_find_mac_rcu(vxlan, cfg, mac, vni);\n \tif (f) {\n \t\tunsigned long now = jiffies;\n \n@@ -416,7 +422,7 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,\n \tlockdep_assert_held_once(\u0026vxlan-\u003ehash_lock);\n \n \trcu_read_lock();\n-\tf = vxlan_find_mac_rcu(vxlan, mac, vni);\n+\tf = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan-\u003ecfg), mac, vni);\n \trcu_read_unlock();\n \n \treturn f;\n@@ -457,7 +463,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,\n \n \trcu_read_lock();\n \n-\tf = vxlan_find_mac_rcu(vxlan, eth_addr, vni);\n+\tf = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan-\u003ecfg), eth_addr, vni);\n \tif (f)\n \t\trdst = first_remote_rcu(f);\n \tif (!rdst) {\n@@ -863,12 +869,13 @@ int vxlan_fdb_create(struct vxlan_dev *vxlan,\n \t\t     u32 nhid, struct vxlan_fdb **fdb,\n \t\t     struct netlink_ext_ack *extack)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct vxlan_rdst *rd = NULL;\n \tstruct vxlan_fdb *f;\n \tint rc;\n \n-\tif (vxlan-\u003ecfg.addrmax \u0026\u0026\n-\t    vxlan-\u003eaddrcnt \u003e= vxlan-\u003ecfg.addrmax)\n+\tif (cfg-\u003eaddrmax \u0026\u0026\n+\t    vxlan-\u003eaddrcnt \u003e= cfg-\u003eaddrmax)\n \t\treturn -ENOSPC;\n \n \tnetdev_dbg(vxlan-\u003edev, \"add %pM -\u003e %pIS\\n\", mac, ip);\n@@ -1148,6 +1155,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,\n \t\t\t   __be32 *vni, u32 *ifindex, u32 *nhid,\n \t\t\t   struct netlink_ext_ack *extack)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct net *net = dev_net(vxlan-\u003edev);\n \tint err;\n \n@@ -1164,7 +1172,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,\n \t\t\treturn err;\n \t\t}\n \t} else {\n-\t\tunion vxlan_addr *remote = \u0026vxlan-\u003edefault_dst.remote_ip;\n+\t\tconst union vxlan_addr *remote = \u0026cfg-\u003eremote_ip;\n \n \t\tif (remote-\u003esa.sa_family == AF_INET) {\n \t\t\tip-\u003esin.sin_addr.s_addr = htonl(INADDR_ANY);\n@@ -1184,7 +1192,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,\n \t\t}\n \t\t*port = nla_get_be16(tb[NDA_PORT]);\n \t} else {\n-\t\t*port = vxlan-\u003ecfg.dst_port;\n+\t\t*port = cfg-\u003edst_port;\n \t}\n \n \tif (tb[NDA_VNI]) {\n@@ -1194,7 +1202,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,\n \t\t}\n \t\t*vni = cpu_to_be32(nla_get_u32(tb[NDA_VNI]));\n \t} else {\n-\t\t*vni = vxlan-\u003edefault_dst.remote_vni;\n+\t\t*vni = cfg-\u003evni;\n \t}\n \n \tif (tb[NDA_SRC_VNI]) {\n@@ -1204,7 +1212,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,\n \t\t}\n \t\t*src_vni = cpu_to_be32(nla_get_u32(tb[NDA_SRC_VNI]));\n \t} else {\n-\t\t*src_vni = vxlan-\u003edefault_dst.remote_vni;\n+\t\t*src_vni = cfg-\u003evni;\n \t}\n \n \tif (tb[NDA_IFINDEX]) {\n@@ -1394,18 +1402,23 @@ static int vxlan_fdb_get(struct sk_buff *skb,\n \t\t\t struct netlink_ext_ack *extack)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg;\n \tstruct vxlan_fdb *f;\n \t__be32 vni;\n \tint err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n \tif (tb[NDA_VNI])\n \t\tvni = cpu_to_be32(nla_get_u32(tb[NDA_VNI]));\n+\telse if (cfg)\n+\t\tvni = cfg-\u003evni;\n \telse\n-\t\tvni = vxlan-\u003edefault_dst.remote_vni;\n+\t\treturn -ENODEV;\n \n \trcu_read_lock();\n \n-\tf = vxlan_find_mac_rcu(vxlan, addr, vni);\n+\tf = vxlan_find_mac_rcu(vxlan, cfg, addr, vni);\n \tif (!f) {\n \t\tNL_SET_ERR_MSG(extack, \"Fdb entry not found\");\n \t\terr = -ENOENT;\n@@ -1423,6 +1436,7 @@ static int vxlan_fdb_get(struct sk_buff *skb,\n  * and Tunnel endpoint.\n  */\n static enum skb_drop_reason vxlan_snoop(struct net_device *dev,\n+\t\t\t\t\tconst struct vxlan_config *cfg,\n \t\t\t\t\tunion vxlan_addr *src_ip,\n \t\t\t\t\tconst u8 *src_mac, u32 src_ifindex,\n \t\t\t\t\t__be32 vni)\n@@ -1441,7 +1455,7 @@ static enum skb_drop_reason vxlan_snoop(struct net_device *dev,\n \t\tifindex = src_ifindex;\n #endif\n \n-\tf = vxlan_find_mac_rcu(vxlan, src_mac, vni);\n+\tf = vxlan_find_mac_rcu(vxlan, cfg, src_mac, vni);\n \tif (likely(f)) {\n \t\tstruct vxlan_rdst *rdst = first_remote_rcu(f);\n \t\tunsigned long now = jiffies;\n@@ -1477,9 +1491,9 @@ static enum skb_drop_reason vxlan_snoop(struct net_device *dev,\n \t\t\tvxlan_fdb_update(vxlan, src_mac, src_ip,\n \t\t\t\t\t NUD_REACHABLE,\n \t\t\t\t\t NLM_F_EXCL|NLM_F_CREATE,\n-\t\t\t\t\t vxlan-\u003ecfg.dst_port,\n+\t\t\t\t\t cfg-\u003edst_port,\n \t\t\t\t\t vni,\n-\t\t\t\t\t vxlan-\u003edefault_dst.remote_vni,\n+\t\t\t\t\t cfg-\u003evni,\n \t\t\t\t\t ifindex, NTF_SELF, 0, true, NULL);\n \t\tspin_unlock(\u0026vxlan-\u003ehash_lock);\n \t}\n@@ -1507,6 +1521,7 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs)\n \n static void vxlan_sock_release(struct vxlan_dev *vxlan)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct vxlan_sock *sock4 = rtnl_dereference(vxlan-\u003evn4_sock);\n #if IS_ENABLED(CONFIG_IPV6)\n \tstruct vxlan_sock *sock6 = rtnl_dereference(vxlan-\u003evn6_sock);\n@@ -1516,7 +1531,7 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan)\n \n \tRCU_INIT_POINTER(vxlan-\u003evn4_sock, NULL);\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)\n+\tif (cfg \u0026\u0026 (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER))\n \t\tvxlan_vs_del_vnigrp(vxlan);\n \telse\n \t\tvxlan_vs_del_dev(vxlan);\n@@ -1587,6 +1602,7 @@ static void vxlan_parse_gbp_hdr(struct sk_buff *skb, u32 vxflags,\n }\n \n static enum skb_drop_reason vxlan_set_mac(struct vxlan_dev *vxlan,\n+\t\t\t\t\t  const struct vxlan_config *cfg,\n \t\t\t\t\t  struct vxlan_sock *vs,\n \t\t\t\t\t  struct sk_buff *skb, __be32 vni)\n {\n@@ -1612,10 +1628,10 @@ static enum skb_drop_reason vxlan_set_mac(struct vxlan_dev *vxlan,\n #endif\n \t}\n \n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_LEARN))\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_LEARN))\n \t\treturn SKB_NOT_DROPPED_YET;\n \n-\treturn vxlan_snoop(skb-\u003edev, \u0026saddr, eth_hdr(skb)-\u003eh_source,\n+\treturn vxlan_snoop(skb-\u003edev, cfg, \u0026saddr, eth_hdr(skb)-\u003eh_source,\n \t\t\t   ifindex, vni);\n }\n \n@@ -1646,18 +1662,21 @@ static bool vxlan_ecn_decapsulate(struct vxlan_sock *vs, void *oiph,\n static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n {\n \tstruct vxlan_vni_node *vninode = NULL;\n-\tconst struct vxlanhdr *vh;\n-\tstruct vxlan_dev *vxlan;\n-\tstruct vxlan_sock *vs;\n-\tstruct vxlan_metadata _md;\n-\tstruct vxlan_metadata *md = \u0026_md;\n+\tconst struct vxlan_config *cfg;\n \t__be16 protocol = htons(ETH_P_TEB);\n \tenum skb_drop_reason reason;\n+\tconst struct vxlanhdr *vh;\n+\tstruct vxlan_metadata *md;\n+\tstruct vxlan_metadata _md;\n+\tstruct vxlan_dev *vxlan;\n \tbool raw_proto = false;\n-\tvoid *oiph;\n+\tstruct vxlan_sock *vs;\n \t__be32 vni = 0;\n+\tvoid *oiph;\n \tint nh;\n \n+\tmd = \u0026_md;\n+\n \t/* Need UDP and VXLAN header to be present */\n \treason = pskb_may_pull_reason(skb, VXLAN_HLEN);\n \tif (reason)\n@@ -1685,8 +1704,10 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \t\tgoto drop;\n \t}\n \n-\tif (vh-\u003evx_flags \u0026 vxlan-\u003ecfg.reserved_bits.vx_flags ||\n-\t    vh-\u003evx_vni \u0026 vxlan-\u003ecfg.reserved_bits.vx_vni) {\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\n+\tif (vh-\u003evx_flags \u0026 cfg-\u003ereserved_bits.vx_flags ||\n+\t    vh-\u003evx_vni \u0026 cfg-\u003ereserved_bits.vx_vni) {\n \t\t/* If the header uses bits besides those enabled by the\n \t\t * netdevice configuration, treat this as a malformed packet.\n \t\t * This behavior diverges from VXLAN RFC (RFC7348) which\n@@ -1698,12 +1719,12 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \t\treason = SKB_DROP_REASON_VXLAN_INVALID_HDR;\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_frame_errors);\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_errors);\n-\t\tvxlan_vnifilter_count(vxlan, vni, vninode,\n+\t\tvxlan_vnifilter_count(vxlan, cfg, vni, vninode,\n \t\t\t\t      VXLAN_VNI_STATS_RX_ERRORS, 0);\n \t\tgoto drop;\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_GPE) {\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_GPE) {\n \t\tif (!vxlan_parse_gpe_proto(vh, \u0026protocol))\n \t\t\tgoto drop;\n \t\traw_proto = true;\n@@ -1715,8 +1736,8 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \t\tgoto drop;\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_REMCSUM_RX) {\n-\t\treason = vxlan_remcsum(skb, vxlan-\u003ecfg.flags);\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_REMCSUM_RX) {\n+\t\treason = vxlan_remcsum(skb, cfg-\u003eflags);\n \t\tif (unlikely(reason))\n \t\t\tgoto drop;\n \t}\n@@ -1741,14 +1762,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \t\tmemset(md, 0, sizeof(*md));\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_GBP)\n-\t\tvxlan_parse_gbp_hdr(skb, vxlan-\u003ecfg.flags, md);\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_GBP)\n+\t\tvxlan_parse_gbp_hdr(skb, cfg-\u003eflags, md);\n \t/* Note that GBP and GPE can never be active together. This is\n \t * ensured in vxlan_dev_configure.\n \t */\n \n \tif (!raw_proto) {\n-\t\treason = vxlan_set_mac(vxlan, vs, skb, vni);\n+\t\treason = vxlan_set_mac(vxlan, cfg, vs, skb, vni);\n \t\tif (reason)\n \t\t\tgoto drop;\n \t} else {\n@@ -1769,7 +1790,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \tif (reason) {\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_length_errors);\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_errors);\n-\t\tvxlan_vnifilter_count(vxlan, vni, vninode,\n+\t\tvxlan_vnifilter_count(vxlan, cfg, vni, vninode,\n \t\t\t\t      VXLAN_VNI_STATS_RX_ERRORS, 0);\n \t\tgoto drop;\n \t}\n@@ -1781,7 +1802,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \t\treason = SKB_DROP_REASON_IP_TUNNEL_ECN;\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_frame_errors);\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_errors);\n-\t\tvxlan_vnifilter_count(vxlan, vni, vninode,\n+\t\tvxlan_vnifilter_count(vxlan, cfg, vni, vninode,\n \t\t\t\t      VXLAN_VNI_STATS_RX_ERRORS, 0);\n \t\tgoto drop;\n \t}\n@@ -1791,14 +1812,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \tif (unlikely(!(vxlan-\u003edev-\u003eflags \u0026 IFF_UP))) {\n \t\trcu_read_unlock();\n \t\tdev_dstats_rx_dropped(vxlan-\u003edev);\n-\t\tvxlan_vnifilter_count(vxlan, vni, vninode,\n+\t\tvxlan_vnifilter_count(vxlan, cfg, vni, vninode,\n \t\t\t\t      VXLAN_VNI_STATS_RX_DROPS, 0);\n \t\treason = SKB_DROP_REASON_DEV_READY;\n \t\tgoto drop;\n \t}\n \n \tdev_dstats_rx_add(vxlan-\u003edev, skb-\u003elen);\n-\tvxlan_vnifilter_count(vxlan, vni, vninode, VXLAN_VNI_STATS_RX, skb-\u003elen);\n+\tvxlan_vnifilter_count(vxlan, cfg, vni, vninode, VXLAN_VNI_STATS_RX, skb-\u003elen);\n \tgro_cells_receive(\u0026vxlan-\u003egro_cells, skb);\n \n \trcu_read_unlock();\n@@ -1839,20 +1860,23 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb)\n \treturn 0;\n }\n \n-static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n+static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg;\n \tstruct arphdr *parp;\n \tu8 *arpptr, *sha;\n \t__be32 sip, tip;\n \tstruct neighbour *n;\n \n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\n \tif (dev-\u003eflags \u0026 IFF_NOARP)\n \t\tgoto out;\n \n \tif (!pskb_may_pull(skb, arp_hdr_len(dev))) {\n \t\tdev_dstats_tx_dropped(dev);\n-\t\tvxlan_vnifilter_count(vxlan, vni, NULL,\n+\t\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL,\n \t\t\t\t      VXLAN_VNI_STATS_TX_DROPS, 0);\n \t\tgoto out;\n \t}\n@@ -1890,7 +1914,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \t\t}\n \n \t\trcu_read_lock();\n-\t\tf = vxlan_find_mac_tx(vxlan, n-\u003eha, vni);\n+\t\tf = vxlan_find_mac_tx(vxlan, cfg, n-\u003eha, vni);\n \t\tif (f)\n \t\t\trdst = first_remote_rcu(f);\n \t\tif (rdst \u0026\u0026 vxlan_addr_any(\u0026rdst-\u003eremote_ip)) {\n@@ -1916,11 +1940,11 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \n \t\tif (netif_rx(reply) == NET_RX_DROP) {\n \t\t\tdev_dstats_rx_dropped(dev);\n-\t\t\tvxlan_vnifilter_count(vxlan, vni, NULL,\n+\t\t\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL,\n \t\t\t\t\t      VXLAN_VNI_STATS_RX_DROPS, 0);\n \t\t}\n \n-\t} else if (vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS) {\n+\t} else if (flags \u0026 VXLAN_F_L3MISS) {\n \t\tunion vxlan_addr ipa = {\n \t\t\t.sin.sin_addr.s_addr = tip,\n \t\t\t.sin.sin_family = AF_INET,\n@@ -2027,9 +2051,10 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,\n \treturn reply;\n }\n \n-static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n+static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg;\n \tconst struct in6_addr *daddr;\n \tconst struct ipv6hdr *iphdr;\n \tstruct neighbour *n;\n@@ -2039,6 +2064,8 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \tif (unlikely(!ipv6_mod_enabled()))\n \t\tgoto out;\n \n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\n \tiphdr = ipv6_hdr(skb);\n \tdaddr = \u0026iphdr-\u003edaddr;\n \tmsg = (struct nd_msg *)(iphdr + 1);\n@@ -2059,7 +2086,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \t\t\tgoto out;\n \t\t}\n \n-\t\tf = vxlan_find_mac_tx(vxlan, n-\u003eha, vni);\n+\t\tf = vxlan_find_mac_tx(vxlan, cfg, n-\u003eha, vni);\n \t\tif (f)\n \t\t\trdst = first_remote_rcu(f);\n \t\tif (rdst \u0026\u0026 vxlan_addr_any(\u0026rdst-\u003eremote_ip)) {\n@@ -2078,10 +2105,10 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \n \t\tif (netif_rx(reply) == NET_RX_DROP) {\n \t\t\tdev_dstats_rx_dropped(dev);\n-\t\t\tvxlan_vnifilter_count(vxlan, vni, NULL,\n+\t\t\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL,\n \t\t\t\t\t      VXLAN_VNI_STATS_RX_DROPS, 0);\n \t\t}\n-\t} else if (vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS) {\n+\t} else if (flags \u0026 VXLAN_F_L3MISS) {\n \t\tunion vxlan_addr ipa = {\n \t\t\t.sin6.sin6_addr = msg-\u003etarget,\n \t\t\t.sin6.sin6_family = AF_INET6,\n@@ -2097,9 +2124,9 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n }\n #endif\n \n-static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)\n+static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb,\n+\t\t\t       const struct vxlan_config *cfg)\n {\n-\tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct neighbour *n;\n \n \tif (is_multicast_ether_addr(eth_hdr(skb)-\u003eh_dest))\n@@ -2115,7 +2142,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)\n \t\t\treturn false;\n \t\tpip = ip_hdr(skb);\n \t\tn = neigh_lookup(\u0026arp_tbl, \u0026pip-\u003edaddr, dev);\n-\t\tif (!n \u0026\u0026 (vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS)) {\n+\t\tif (!n \u0026\u0026 (cfg-\u003eflags \u0026 VXLAN_F_L3MISS)) {\n \t\t\tunion vxlan_addr ipa = {\n \t\t\t\t.sin.sin_addr.s_addr = pip-\u003edaddr,\n \t\t\t\t.sin.sin_family = AF_INET,\n@@ -2141,7 +2168,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)\n \t\t\treturn false;\n \t\tpip6 = ipv6_hdr(skb);\n \t\tn = neigh_lookup(\u0026nd_tbl, \u0026pip6-\u003edaddr, dev);\n-\t\tif (!n \u0026\u0026 (vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS)) {\n+\t\tif (!n \u0026\u0026 (cfg-\u003eflags \u0026 VXLAN_F_L3MISS)) {\n \t\t\tunion vxlan_addr ipa = {\n \t\t\t\t.sin6.sin6_addr = pip6-\u003edaddr,\n \t\t\t\t.sin6.sin6_family = AF_INET6,\n@@ -2253,20 +2280,20 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst,\n \n /* Bypass encapsulation if the destination is local */\n static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,\n-\t\t\t       struct vxlan_dev *dst_vxlan, __be32 vni,\n-\t\t\t       bool snoop)\n+\t\t\t       struct vxlan_dev *dst_vxlan,\n+\t\t\t       const struct vxlan_config *cfg,\n+\t\t\t       __be32 vni, bool snoop)\n {\n \tunion vxlan_addr loopback;\n-\tunion vxlan_addr *remote_ip = \u0026dst_vxlan-\u003edefault_dst.remote_ip;\n \tunsigned int len = skb-\u003elen;\n-\tstruct net_device *dev;\n+\tstruct net_device *dev = dst_vxlan-\u003edev;\n \n \tskb-\u003epkt_type = PACKET_HOST;\n \tskb-\u003eencapsulation = 0;\n-\tskb-\u003edev = dst_vxlan-\u003edev;\n+\tskb-\u003edev = dev;\n \t__skb_pull(skb, skb_network_offset(skb));\n \n-\tif (remote_ip-\u003esa.sa_family == AF_INET) {\n+\tif (cfg-\u003eremote_ip.sa.sa_family == AF_INET) {\n \t\tloopback.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\n \t\tloopback.sa.sa_family =  AF_INET;\n #if IS_ENABLED(CONFIG_IPV6)\n@@ -2277,26 +2304,25 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,\n \t}\n \n \trcu_read_lock();\n-\tdev = skb-\u003edev;\n \tif (unlikely(!(dev-\u003eflags \u0026 IFF_UP))) {\n \t\tkfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);\n \t\tgoto drop;\n \t}\n \n-\tif ((dst_vxlan-\u003ecfg.flags \u0026 VXLAN_F_LEARN) \u0026\u0026 snoop)\n-\t\tvxlan_snoop(dev, \u0026loopback, eth_hdr(skb)-\u003eh_source, 0, vni);\n+\tif ((cfg-\u003eflags \u0026 VXLAN_F_LEARN) \u0026\u0026 snoop)\n+\t\tvxlan_snoop(dev, cfg, \u0026loopback, eth_hdr(skb)-\u003eh_source, 0, vni);\n \n \tdev_dstats_tx_add(src_vxlan-\u003edev, len);\n-\tvxlan_vnifilter_count(src_vxlan, vni, NULL, VXLAN_VNI_STATS_TX, len);\n+\tvxlan_vnifilter_count(src_vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX, len);\n \n \tif (__netif_rx(skb) == NET_RX_SUCCESS) {\n \t\tdev_dstats_rx_add(dst_vxlan-\u003edev, len);\n-\t\tvxlan_vnifilter_count(dst_vxlan, vni, NULL, VXLAN_VNI_STATS_RX,\n+\t\tvxlan_vnifilter_count(dst_vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_RX,\n \t\t\t\t      len);\n \t} else {\n drop:\n \t\tdev_dstats_rx_dropped(dev);\n-\t\tvxlan_vnifilter_count(dst_vxlan, vni, NULL,\n+\t\tvxlan_vnifilter_count(dst_vxlan, cfg, vni, NULL,\n \t\t\t\t      VXLAN_VNI_STATS_RX_DROPS, 0);\n \t}\n \trcu_read_unlock();\n@@ -2304,6 +2330,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,\n \n static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,\n \t\t\t\t struct vxlan_dev *vxlan,\n+\t\t\t\t const struct vxlan_config *cfg,\n \t\t\t\t int addr_family,\n \t\t\t\t __be16 dst_port, int dst_ifindex, __be32 vni,\n \t\t\t\t struct dst_entry *dst,\n@@ -2319,22 +2346,22 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,\n \t/* Bypass encapsulation if the destination is local */\n \tif (rt_flags \u0026 RTCF_LOCAL \u0026\u0026\n \t    !(rt_flags \u0026 (RTCF_BROADCAST | RTCF_MULTICAST)) \u0026\u0026\n-\t    vxlan-\u003ecfg.flags \u0026 VXLAN_F_LOCALBYPASS) {\n+\t    cfg-\u003eflags \u0026 VXLAN_F_LOCALBYPASS) {\n \t\tstruct vxlan_dev *dst_vxlan;\n \n \t\tdst_release(dst);\n \t\tdst_vxlan = vxlan_find_vni(vxlan-\u003enet, dst_ifindex, vni,\n \t\t\t\t\t   addr_family, dst_port,\n-\t\t\t\t\t   vxlan-\u003ecfg.flags);\n+\t\t\t\t\t   cfg-\u003eflags);\n \t\tif (!dst_vxlan) {\n \t\t\tDEV_STATS_INC(dev, tx_errors);\n-\t\t\tvxlan_vnifilter_count(vxlan, vni, NULL,\n+\t\t\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL,\n \t\t\t\t\t      VXLAN_VNI_STATS_TX_ERRORS, 0);\n \t\t\tkfree_skb_reason(skb, SKB_DROP_REASON_VXLAN_VNI_NOT_FOUND);\n \n \t\t\treturn -ENOENT;\n \t\t}\n-\t\tvxlan_encap_bypass(skb, vxlan, dst_vxlan, vni, true);\n+\t\tvxlan_encap_bypass(skb, vxlan, dst_vxlan, cfg, vni, true);\n \t\treturn 1;\n \t}\n \n@@ -2342,30 +2369,35 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,\n }\n \n void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n+\t\t    const struct vxlan_config *cfg,\n \t\t    __be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc)\n {\n+\tunsigned int pkt_len = skb-\u003elen;\n+\t__be16 src_port = 0, dst_port;\n+\tstruct dst_entry *ndst = NULL;\n+\tenum skb_drop_reason reason;\n+\tstruct vxlan_dev *vxlan;\n \tstruct dst_cache *dst_cache;\n+\tconst struct iphdr *old_iph;\n \tstruct ip_tunnel_info *info;\n \tstruct ip_tunnel_key *pkey;\n-\tstruct ip_tunnel_key key;\n-\tstruct vxlan_dev *vxlan = netdev_priv(dev);\n-\tconst struct iphdr *old_iph;\n+\tstruct vxlan_metadata *md;\n \tstruct vxlan_metadata _md;\n-\tstruct vxlan_metadata *md = \u0026_md;\n-\tunsigned int pkt_len = skb-\u003elen;\n-\t__be16 src_port = 0, dst_port;\n-\tstruct dst_entry *ndst = NULL;\n+\tstruct ip_tunnel_key key;\n+\tu32 flags = cfg-\u003eflags;\n+\tbool udp_sum = false;\n+\tbool no_eth_encap;\n \tint addr_family;\n+\tbool use_cache;\n+\t__be32 vni = 0;\n+\tbool xnet;\n \t__u8 tos, ttl;\n \tint ifindex;\n \tint err = 0;\n-\tu32 flags = vxlan-\u003ecfg.flags;\n-\tbool use_cache;\n-\tbool udp_sum = false;\n-\tbool xnet = !net_eq(vxlan-\u003enet, dev_net(vxlan-\u003edev));\n-\tenum skb_drop_reason reason;\n-\tbool no_eth_encap;\n-\t__be32 vni = 0;\n+\n+\tvxlan = netdev_priv(dev);\n+\txnet = !net_eq(vxlan-\u003enet, dev_net(vxlan-\u003edev));\n+\tmd = \u0026_md;\n \n \tno_eth_encap = flags \u0026 VXLAN_F_GPE \u0026\u0026 skb-\u003eprotocol != htons(ETH_P_TEB);\n \treason = skb_vlan_inet_prepare(skb, no_eth_encap);\n@@ -2385,23 +2417,23 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\tif (vxlan_addr_any(\u0026rdst-\u003eremote_ip)) {\n \t\t\tif (did_rsc) {\n \t\t\t\t/* short-circuited back to local bridge */\n-\t\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan,\n+\t\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan, cfg,\n \t\t\t\t\t\t   default_vni, true);\n \t\t\t\treturn;\n \t\t\t}\n \t\t\tgoto drop;\n \t\t}\n \n-\t\taddr_family = vxlan-\u003ecfg.saddr.sa.sa_family;\n-\t\tdst_port = rdst-\u003eremote_port ? rdst-\u003eremote_port : vxlan-\u003ecfg.dst_port;\n+\t\taddr_family = cfg-\u003esaddr.sa.sa_family;\n+\t\tdst_port = rdst-\u003eremote_port ? rdst-\u003eremote_port : cfg-\u003edst_port;\n \t\tvni = (rdst-\u003eremote_vni) ? : default_vni;\n \t\tifindex = rdst-\u003eremote_ifindex;\n \n \t\tif (addr_family == AF_INET) {\n-\t\t\tkey.u.ipv4.src = vxlan-\u003ecfg.saddr.sin.sin_addr.s_addr;\n+\t\t\tkey.u.ipv4.src = cfg-\u003esaddr.sin.sin_addr.s_addr;\n \t\t\tkey.u.ipv4.dst = rdst-\u003eremote_ip.sin.sin_addr.s_addr;\n \t\t} else {\n-\t\t\tkey.u.ipv6.src = vxlan-\u003ecfg.saddr.sin6.sin6_addr;\n+\t\t\tkey.u.ipv6.src = cfg-\u003esaddr.sin6.sin6_addr;\n \t\t\tkey.u.ipv6.dst = rdst-\u003eremote_ip.sin6.sin6_addr;\n \t\t}\n \n@@ -2410,11 +2442,11 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\tif (flags \u0026 VXLAN_F_TTL_INHERIT) {\n \t\t\tttl = ip_tunnel_get_ttl(old_iph, skb);\n \t\t} else {\n-\t\t\tttl = vxlan-\u003ecfg.ttl;\n+\t\t\tttl = cfg-\u003ettl;\n \t\t\tif (!ttl \u0026\u0026 vxlan_addr_multicast(\u0026rdst-\u003eremote_ip))\n \t\t\t\tttl = 1;\n \t\t}\n-\t\ttos = vxlan-\u003ecfg.tos;\n+\t\ttos = cfg-\u003etos;\n \t\tif (tos == 1)\n \t\t\ttos = ip_tunnel_get_dsfield(old_iph, skb);\n \t\tif (tos \u0026\u0026 !info)\n@@ -2425,9 +2457,9 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\telse\n \t\t\tudp_sum = !(flags \u0026 VXLAN_F_UDP_ZERO_CSUM6_TX);\n #if IS_ENABLED(CONFIG_IPV6)\n-\t\tswitch (vxlan-\u003ecfg.label_policy) {\n+\t\tswitch (cfg-\u003elabel_policy) {\n \t\tcase VXLAN_LABEL_FIXED:\n-\t\t\tkey.label = vxlan-\u003ecfg.label;\n+\t\t\tkey.label = cfg-\u003elabel;\n \t\t\tbreak;\n \t\tcase VXLAN_LABEL_INHERIT:\n \t\t\tkey.label = ip_tunnel_get_flowlabel(old_iph, skb);\n@@ -2445,7 +2477,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\t}\n \t\tpkey = \u0026info-\u003ekey;\n \t\taddr_family = ip_tunnel_info_af(info);\n-\t\tdst_port = info-\u003ekey.tp_dst ? : vxlan-\u003ecfg.dst_port;\n+\t\tdst_port = info-\u003ekey.tp_dst ? : cfg-\u003edst_port;\n \t\tvni = tunnel_id_to_key32(info-\u003ekey.tun_id);\n \t\tifindex = 0;\n \t\tdst_cache = \u0026info-\u003edst_cache;\n@@ -2458,8 +2490,8 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\ttos = info-\u003ekey.tos;\n \t\tudp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info-\u003ekey.tun_flags);\n \t}\n-\tsrc_port = udp_flow_src_port(dev_net(dev), skb, vxlan-\u003ecfg.port_min,\n-\t\t\t\t     vxlan-\u003ecfg.port_max, true);\n+\tsrc_port = udp_flow_src_port(dev_net(dev), skb, cfg-\u003eport_min,\n+\t\t\t\t     cfg-\u003eport_max, true);\n \n \trcu_read_lock();\n \tif (addr_family == AF_INET) {\n@@ -2492,15 +2524,15 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \n \t\tif (!info) {\n \t\t\t/* Bypass encapsulation if the destination is local */\n-\t\t\terr = encap_bypass_if_local(skb, dev, vxlan, AF_INET,\n+\t\t\terr = encap_bypass_if_local(skb, dev, vxlan, cfg, AF_INET,\n \t\t\t\t\t\t    dst_port, ifindex, vni,\n \t\t\t\t\t\t    \u0026rt-\u003edst, rt-\u003ert_flags);\n \t\t\tif (err)\n \t\t\t\tgoto out_unlock;\n \n-\t\t\tif (vxlan-\u003ecfg.df == VXLAN_DF_SET) {\n+\t\t\tif (cfg-\u003edf == VXLAN_DF_SET) {\n \t\t\t\tdf = htons(IP_DF);\n-\t\t\t} else if (vxlan-\u003ecfg.df == VXLAN_DF_INHERIT) {\n+\t\t\t} else if (cfg-\u003edf == VXLAN_DF_INHERIT) {\n \t\t\t\tstruct ethhdr *eth = eth_hdr(skb);\n \n \t\t\t\tif (ntohs(eth-\u003eh_proto) == ETH_P_IPV6 ||\n@@ -2529,7 +2561,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\t\t\tunclone-\u003ekey.u.ipv4.src = pkey-\u003eu.ipv4.dst;\n \t\t\t\tunclone-\u003ekey.u.ipv4.dst = saddr;\n \t\t\t}\n-\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan, vni, false);\n+\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan, cfg, vni, false);\n \t\t\tdst_release(ndst);\n \t\t\tgoto out_unlock;\n \t\t}\n@@ -2579,7 +2611,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\tif (!info) {\n \t\t\tu32 rt6i_flags = dst_rt6_info(ndst)-\u003ert6i_flags;\n \n-\t\t\terr = encap_bypass_if_local(skb, dev, vxlan, AF_INET6,\n+\t\t\terr = encap_bypass_if_local(skb, dev, vxlan, cfg, AF_INET6,\n \t\t\t\t\t\t    dst_port, ifindex, vni,\n \t\t\t\t\t\t    ndst, rt6i_flags);\n \t\t\tif (err)\n@@ -2603,7 +2635,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\t\t\tunclone-\u003ekey.u.ipv6.dst = saddr;\n \t\t\t}\n \n-\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan, vni, false);\n+\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan, cfg, vni, false);\n \t\t\tdst_release(ndst);\n \t\t\tgoto out_unlock;\n \t\t}\n@@ -2624,14 +2656,14 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\t\t\t     ip6cb_flags);\n #endif\n \t}\n-\tvxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX, pkt_len);\n+\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX, pkt_len);\n out_unlock:\n \trcu_read_unlock();\n \treturn;\n \n drop:\n \tdev_dstats_tx_dropped(dev);\n-\tvxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0);\n+\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0);\n \tkfree_skb_reason(skb, reason);\n \treturn;\n \n@@ -2643,11 +2675,12 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\tDEV_STATS_INC(dev, tx_carrier_errors);\n \tdst_release(ndst);\n \tDEV_STATS_INC(dev, tx_errors);\n-\tvxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0);\n+\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0);\n \tkfree_skb_reason(skb, reason);\n }\n \n static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,\n+\t\t\t  const struct vxlan_config *cfg,\n \t\t\t  struct vxlan_fdb *f, __be32 vni, bool did_rsc)\n {\n \tstruct vxlan_rdst nh_rdst;\n@@ -2664,7 +2697,7 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,\n \tdo_xmit = vxlan_fdb_nh_path_select(nh, hash, \u0026nh_rdst);\n \n \tif (likely(do_xmit))\n-\t\tvxlan_xmit_one(skb, dev, vni, \u0026nh_rdst, did_rsc);\n+\t\tvxlan_xmit_one(skb, dev, cfg, vni, \u0026nh_rdst, did_rsc);\n \telse\n \t\tgoto drop;\n \n@@ -2672,15 +2705,15 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,\n \n drop:\n \tdev_dstats_tx_dropped(dev);\n-\tvxlan_vnifilter_count(netdev_priv(dev), vni, NULL,\n+\tvxlan_vnifilter_count(netdev_priv(dev), cfg, vni, NULL,\n \t\t\t      VXLAN_VNI_STATS_TX_DROPS, 0);\n \tdev_kfree_skb(skb);\n }\n \n static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,\n-\t\t\t\t   u32 nhid, __be32 vni)\n+\t\t\t\t   u32 nhid, __be32 vni, int saddr_family,\n+\t\t\t\t   const struct vxlan_config *cfg)\n {\n-\tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct vxlan_rdst nh_rdst;\n \tstruct nexthop *nh;\n \tbool do_xmit;\n@@ -2698,11 +2731,11 @@ static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,\n \tdo_xmit = vxlan_fdb_nh_path_select(nh, hash, \u0026nh_rdst);\n \trcu_read_unlock();\n \n-\tif (vxlan-\u003ecfg.saddr.sa.sa_family != nh_rdst.remote_ip.sa.sa_family)\n+\tif (saddr_family != nh_rdst.remote_ip.sa.sa_family)\n \t\tgoto drop;\n \n \tif (likely(do_xmit))\n-\t\tvxlan_xmit_one(skb, dev, vni, \u0026nh_rdst, false);\n+\t\tvxlan_xmit_one(skb, dev, cfg, vni, \u0026nh_rdst, false);\n \telse\n \t\tgoto drop;\n \n@@ -2710,7 +2743,7 @@ static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,\n \n drop:\n \tdev_dstats_tx_dropped(dev);\n-\tvxlan_vnifilter_count(netdev_priv(dev), vni, NULL,\n+\tvxlan_vnifilter_count(netdev_priv(dev), cfg, vni, NULL,\n \t\t\t      VXLAN_VNI_STATS_TX_DROPS, 0);\n \tdev_kfree_skb(skb);\n \treturn NETDEV_TX_OK;\n@@ -2727,34 +2760,45 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct vxlan_rdst *rdst, *fdst = NULL;\n \tconst struct ip_tunnel_info *info;\n+\tconst struct vxlan_config *cfg;\n+\t__be32 default_vni;\n \tstruct vxlan_fdb *f;\n \tstruct ethhdr *eth;\n+\tint saddr_family;\n \t__be32 vni = 0;\n-\tu32 nhid = 0;\n \tbool did_rsc;\n+\tu32 nhid = 0;\n+\tu32 flags;\n+\n+\trcu_read_lock();\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\tflags = cfg-\u003eflags;\n+\tdefault_vni = cfg-\u003evni;\n+\tsaddr_family = cfg-\u003esaddr.sa.sa_family;\n+\trcu_read_unlock();\n \n \tinfo = skb_tunnel_info(skb);\n \n \tskb_reset_mac_header(skb);\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA) {\n+\tif (flags \u0026 VXLAN_F_COLLECT_METADATA) {\n \t\tif (info \u0026\u0026 info-\u003emode \u0026 IP_TUNNEL_INFO_BRIDGE \u0026\u0026\n \t\t    info-\u003emode \u0026 IP_TUNNEL_INFO_TX) {\n \t\t\tvni = tunnel_id_to_key32(info-\u003ekey.tun_id);\n \t\t\tnhid = info-\u003ekey.nhid;\n \t\t} else {\n \t\t\tif (info \u0026\u0026 info-\u003emode \u0026 IP_TUNNEL_INFO_TX)\n-\t\t\t\tvxlan_xmit_one(skb, dev, vni, NULL, false);\n+\t\t\t\tvxlan_xmit_one(skb, dev, cfg, vni, NULL, false);\n \t\t\telse\n \t\t\t\tkfree_skb_reason(skb, SKB_DROP_REASON_TUNNEL_TXINFO);\n \t\t\treturn NETDEV_TX_OK;\n \t\t}\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_PROXY) {\n+\tif (flags \u0026 VXLAN_F_PROXY) {\n \t\teth = eth_hdr(skb);\n \t\tif (ntohs(eth-\u003eh_proto) == ETH_P_ARP)\n-\t\t\treturn arp_reduce(dev, skb, vni);\n+\t\t\treturn arp_reduce(dev, skb, vni, flags);\n #if IS_ENABLED(CONFIG_IPV6)\n \t\telse if (ntohs(eth-\u003eh_proto) == ETH_P_IPV6 \u0026\u0026\n \t\t\t pskb_may_pull(skb, sizeof(struct ipv6hdr) +\n@@ -2764,15 +2808,15 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)\n \n \t\t\tif (m-\u003eicmph.icmp6_code == 0 \u0026\u0026\n \t\t\t    m-\u003eicmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)\n-\t\t\t\treturn neigh_reduce(dev, skb, vni);\n+\t\t\t\treturn neigh_reduce(dev, skb, vni, flags);\n \t\t}\n #endif\n \t}\n \n \tif (nhid)\n-\t\treturn vxlan_xmit_nhid(skb, dev, nhid, vni);\n+\t\treturn vxlan_xmit_nhid(skb, dev, nhid, vni, saddr_family, cfg);\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_MDB) {\n+\tif (flags \u0026 VXLAN_F_MDB) {\n \t\tstruct vxlan_mdb_entry *mdb_entry;\n \n \t\trcu_read_lock();\n@@ -2789,26 +2833,26 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)\n \n \teth = eth_hdr(skb);\n \trcu_read_lock();\n-\tf = vxlan_find_mac_tx(vxlan, eth-\u003eh_dest, vni);\n+\tf = vxlan_find_mac_tx(vxlan, cfg, eth-\u003eh_dest, vni);\n \tdid_rsc = false;\n \n-\tif (f \u0026\u0026 (f-\u003eflags \u0026 NTF_ROUTER) \u0026\u0026 (vxlan-\u003ecfg.flags \u0026 VXLAN_F_RSC) \u0026\u0026\n+\tif (f \u0026\u0026 (f-\u003eflags \u0026 NTF_ROUTER) \u0026\u0026 (flags \u0026 VXLAN_F_RSC) \u0026\u0026\n \t    (ntohs(eth-\u003eh_proto) == ETH_P_IP ||\n \t     ntohs(eth-\u003eh_proto) == ETH_P_IPV6)) {\n-\t\tdid_rsc = route_shortcircuit(dev, skb);\n+\t\tdid_rsc = route_shortcircuit(dev, skb, cfg);\n \t\tif (did_rsc)\n-\t\t\tf = vxlan_find_mac_tx(vxlan, eth-\u003eh_dest, vni);\n+\t\t\tf = vxlan_find_mac_tx(vxlan, cfg, eth-\u003eh_dest, vni);\n \t}\n \n \tif (f == NULL) {\n-\t\tf = vxlan_find_mac_tx(vxlan, all_zeros_mac, vni);\n+\t\tf = vxlan_find_mac_tx(vxlan, cfg, all_zeros_mac, vni);\n \t\tif (f == NULL) {\n-\t\t\tif ((vxlan-\u003ecfg.flags \u0026 VXLAN_F_L2MISS) \u0026\u0026\n+\t\t\tif ((flags \u0026 VXLAN_F_L2MISS) \u0026\u0026\n \t\t\t    !is_multicast_ether_addr(eth-\u003eh_dest))\n \t\t\t\tvxlan_fdb_miss(vxlan, eth-\u003eh_dest);\n \n \t\t\tdev_dstats_tx_dropped(dev);\n-\t\t\tvxlan_vnifilter_count(vxlan, vni, NULL,\n+\t\t\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL,\n \t\t\t\t\t      VXLAN_VNI_STATS_TX_DROPS, 0);\n \t\t\tkfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);\n \t\t\tgoto out;\n@@ -2816,8 +2860,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)\n \t}\n \n \tif (rcu_access_pointer(f-\u003enh)) {\n-\t\tvxlan_xmit_nh(skb, dev, f,\n-\t\t\t      (vni ? : vxlan-\u003edefault_dst.remote_vni), did_rsc);\n+\t\tvxlan_xmit_nh(skb, dev, cfg, f,\n+\t\t\t      (vni ? : default_vni), did_rsc);\n \t} else {\n \t\tlist_for_each_entry_rcu(rdst, \u0026f-\u003eremotes, list) {\n \t\t\tstruct sk_buff *skb1;\n@@ -2828,10 +2872,10 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)\n \t\t\t}\n \t\t\tskb1 = skb_clone(skb, GFP_ATOMIC);\n \t\t\tif (skb1)\n-\t\t\t\tvxlan_xmit_one(skb1, dev, vni, rdst, did_rsc);\n+\t\t\t\tvxlan_xmit_one(skb1, dev, cfg, vni, rdst, did_rsc);\n \t\t}\n \t\tif (fdst)\n-\t\t\tvxlan_xmit_one(skb, dev, vni, fdst, did_rsc);\n+\t\t\tvxlan_xmit_one(skb, dev, cfg, vni, fdst, did_rsc);\n \t\telse\n \t\t\tkfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);\n \t}\n@@ -2846,12 +2890,17 @@ static void vxlan_cleanup(struct timer_list *t)\n {\n \tstruct vxlan_dev *vxlan = timer_container_of(vxlan, t, age_timer);\n \tunsigned long next_timer = jiffies + FDB_AGE_INTERVAL;\n+\tconst struct vxlan_config *cfg;\n \tstruct vxlan_fdb *f;\n \n \tif (!netif_running(vxlan-\u003edev))\n \t\treturn;\n \n \trcu_read_lock();\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\tif (!cfg)\n+\t\tgoto out;\n+\n \thlist_for_each_entry_rcu(f, \u0026vxlan-\u003efdb_list, fdb_node) {\n \t\tunsigned long timeout;\n \n@@ -2861,7 +2910,7 @@ static void vxlan_cleanup(struct timer_list *t)\n \t\tif (f-\u003eflags \u0026 NTF_EXT_LEARNED)\n \t\t\tcontinue;\n \n-\t\ttimeout = READ_ONCE(f-\u003eupdated) + vxlan-\u003ecfg.age_interval * HZ;\n+\t\ttimeout = READ_ONCE(f-\u003eupdated) + cfg-\u003eage_interval * HZ;\n \t\tif (time_before_eq(timeout, jiffies)) {\n \t\t\tspin_lock(\u0026vxlan-\u003ehash_lock);\n \t\t\tif (!hlist_unhashed(\u0026f-\u003efdb_node)) {\n@@ -2875,6 +2924,7 @@ static void vxlan_cleanup(struct timer_list *t)\n \t\t\tnext_timer = timeout;\n \t\t}\n \t}\n+out:\n \trcu_read_unlock();\n \n \tmod_timer(\u0026vxlan-\u003eage_timer, next_timer);\n@@ -2905,13 +2955,16 @@ static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan,\n static int vxlan_init(struct net_device *dev)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg;\n \tint err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n \terr = rhashtable_init(\u0026vxlan-\u003efdb_hash_tbl, \u0026vxlan_fdb_rht_params);\n \tif (err)\n \t\treturn err;\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER) {\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER) {\n \t\terr = vxlan_vnigroup_init(vxlan);\n \t\tif (err)\n \t\t\tgoto err_rhashtable_destroy;\n@@ -2931,7 +2984,7 @@ static int vxlan_init(struct net_device *dev)\n err_gro_cells_destroy:\n \tgro_cells_destroy(\u0026vxlan-\u003egro_cells);\n err_vnigroup_uninit:\n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER)\n \t\tvxlan_vnigroup_uninit(vxlan);\n err_rhashtable_destroy:\n \trhashtable_destroy(\u0026vxlan-\u003efdb_hash_tbl);\n@@ -2941,10 +2994,13 @@ static int vxlan_init(struct net_device *dev)\n static void vxlan_uninit(struct net_device *dev)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg;\n+\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n \n \tvxlan_mdb_fini(vxlan);\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)\n+\tif (cfg \u0026\u0026 (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER))\n \t\tvxlan_vnigroup_uninit(vxlan);\n \n \tgro_cells_destroy(\u0026vxlan-\u003egro_cells);\n@@ -2956,6 +3012,7 @@ static void vxlan_uninit(struct net_device *dev)\n static int vxlan_open(struct net_device *dev)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg;\n \tint ret;\n \n \tret = vxlan_sock_add(vxlan);\n@@ -2968,7 +3025,8 @@ static int vxlan_open(struct net_device *dev)\n \t\treturn ret;\n \t}\n \n-\tif (vxlan-\u003ecfg.age_interval)\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tif (cfg \u0026\u0026 cfg-\u003eage_interval)\n \t\tmod_timer(\u0026vxlan-\u003eage_timer, jiffies + FDB_AGE_INTERVAL);\n \n \treturn ret;\n@@ -2990,8 +3048,10 @@ struct vxlan_fdb_flush_desc {\n static bool vxlan_fdb_is_default_entry(const struct vxlan_fdb *f,\n \t\t\t\t       const struct vxlan_dev *vxlan)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n \treturn is_zero_ether_addr(f-\u003ekey.eth_addr) \u0026\u0026\n-\t       f-\u003ekey.vni == vxlan-\u003ecfg.vni;\n+\t       f-\u003ekey.vni == cfg-\u003evni;\n }\n \n static bool vxlan_fdb_nhid_matches(const struct vxlan_fdb *f, u32 nhid)\n@@ -3208,14 +3268,18 @@ static int vxlan_change_mtu(struct net_device *dev, int new_mtu)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n-\tstruct net_device *lowerdev = __dev_get_by_index(vxlan-\u003enet,\n-\t\t\t\t\t\t\t dst-\u003eremote_ifindex);\n+\tconst struct vxlan_config *cfg;\n+\tstruct net_device *lowerdev;\n+\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n+\tlowerdev = __dev_get_by_index(vxlan-\u003enet, dst-\u003eremote_ifindex);\n \n \t/* This check is different than dev-\u003emax_mtu, because it looks at\n \t * the lowerdev-\u003emtu, rather than the static dev-\u003emax_mtu\n \t */\n \tif (lowerdev) {\n-\t\tint max_mtu = lowerdev-\u003emtu - vxlan_headroom(vxlan-\u003ecfg.flags);\n+\t\tint max_mtu = lowerdev-\u003emtu - vxlan_headroom(cfg-\u003eflags);\n \t\tif (new_mtu \u003e max_mtu)\n \t\t\treturn -EINVAL;\n \t}\n@@ -3228,11 +3292,14 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct ip_tunnel_info *info = skb_tunnel_info(skb);\n+\tconst struct vxlan_config *cfg;\n \t__be16 sport, dport;\n \n-\tsport = udp_flow_src_port(dev_net(dev), skb, vxlan-\u003ecfg.port_min,\n-\t\t\t\t  vxlan-\u003ecfg.port_max, true);\n-\tdport = info-\u003ekey.tp_dst ? : vxlan-\u003ecfg.dst_port;\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\n+\tsport = udp_flow_src_port(dev_net(dev), skb, cfg-\u003eport_min,\n+\t\t\t\t  cfg-\u003eport_max, true);\n+\tdport = info-\u003ekey.tp_dst ? : cfg-\u003edst_port;\n \n \tif (ip_tunnel_info_af(info) == AF_INET) {\n \t\tstruct vxlan_sock *sock4 = rcu_dereference(vxlan-\u003evn4_sock);\n@@ -3342,6 +3409,14 @@ static void vxlan_offload_rx_ports(struct net_device *dev, bool push)\n \t}\n }\n \n+static void vxlan_free_dev(struct net_device *dev)\n+{\n+\tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tstruct vxlan_config *cfg = rcu_dereference_protected(vxlan-\u003ecfg, 1);\n+\n+\tkfree(cfg);\n+}\n+\n /* Initialize the device structure. */\n static void vxlan_setup(struct net_device *dev)\n {\n@@ -3350,6 +3425,8 @@ static void vxlan_setup(struct net_device *dev)\n \teth_hw_addr_random(dev);\n \tether_setup(dev);\n \n+\tdev-\u003epriv_destructor = vxlan_free_dev;\n+\n \tdev-\u003eneeds_free_netdev = true;\n \tSET_NETDEV_DEVTYPE(dev, \u0026vxlan_type);\n \n@@ -3630,21 +3707,22 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,\n \n static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)\n {\n-\tbool metadata = vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA;\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tbool metadata = cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA;\n \tstruct vxlan_sock *vs = NULL;\n \tstruct vxlan_dev_node *node;\n \tint l3mdev_index = 0;\n \n \tASSERT_RTNL();\n \n-\tif (vxlan-\u003ecfg.remote_ifindex)\n+\tif (cfg-\u003eremote_ifindex)\n \t\tl3mdev_index = l3mdev_master_upper_ifindex_by_index(\n-\t\t\tvxlan-\u003enet, vxlan-\u003ecfg.remote_ifindex);\n+\t\t\tvxlan-\u003enet, cfg-\u003eremote_ifindex);\n \n-\tif (!vxlan-\u003ecfg.no_share) {\n+\tif (!cfg-\u003eno_share) {\n \t\trcu_read_lock();\n \t\tvs = vxlan_find_sock(vxlan-\u003enet, ipv6 ? AF_INET6 : AF_INET,\n-\t\t\t\t     vxlan-\u003ecfg.dst_port, vxlan-\u003ecfg.flags,\n+\t\t\t\t     cfg-\u003edst_port, cfg-\u003eflags,\n \t\t\t\t     l3mdev_index);\n \t\tif (vs \u0026\u0026 !refcount_inc_not_zero(\u0026vs-\u003erefcnt)) {\n \t\t\trcu_read_unlock();\n@@ -3654,7 +3732,7 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)\n \t}\n \tif (!vs)\n \t\tvs = vxlan_socket_create(vxlan-\u003enet, ipv6,\n-\t\t\t\t\t vxlan-\u003ecfg.dst_port, vxlan-\u003ecfg.flags,\n+\t\t\t\t\t cfg-\u003edst_port, cfg-\u003eflags,\n \t\t\t\t\t l3mdev_index);\n \tif (IS_ERR(vs))\n \t\treturn PTR_ERR(vs);\n@@ -3669,7 +3747,7 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)\n \t\tnode = \u0026vxlan-\u003ehlist4;\n \t}\n \n-\tif (metadata \u0026\u0026 (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER))\n+\tif (metadata \u0026\u0026 (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER))\n \t\tvxlan_vs_add_vnigrp(vxlan, vs, ipv6);\n \telse\n \t\tvxlan_vs_add_dev(vs, vxlan, node);\n@@ -3679,11 +3757,14 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)\n \n static int vxlan_sock_add(struct vxlan_dev *vxlan)\n {\n-\tbool metadata = vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA;\n-\tbool ipv6 = vxlan-\u003ecfg.flags \u0026 VXLAN_F_IPV6 || metadata;\n-\tbool ipv4 = !ipv6 || metadata;\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tbool metadata, ipv6, ipv4;\n \tint ret = 0;\n \n+\tmetadata = cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA;\n+\tipv6 = (cfg-\u003eflags \u0026 VXLAN_F_IPV6) || metadata;\n+\tipv4 = !ipv6 || metadata;\n+\n \tRCU_INIT_POINTER(vxlan-\u003evn4_sock, NULL);\n #if IS_ENABLED(CONFIG_IPV6)\n \tRCU_INIT_POINTER(vxlan-\u003evn6_sock, NULL);\n@@ -3701,28 +3782,33 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan)\n }\n \n int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan,\n-\t\t     struct vxlan_config *conf, __be32 vni)\n+\t\t     const struct vxlan_config *conf, __be32 vni)\n {\n \tstruct vxlan_net *vn = net_generic(src_net, vxlan_net_id);\n \tstruct vxlan_dev *tmp;\n \n \tlist_for_each_entry(tmp, \u0026vn-\u003evxlan_list, next) {\n+\t\tconst struct vxlan_config *tmp_cfg;\n+\n \t\tif (tmp == vxlan)\n \t\t\tcontinue;\n-\t\tif (tmp-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER) {\n+\n+\t\ttmp_cfg = rtnl_dereference(tmp-\u003ecfg);\n+\n+\t\tif (tmp_cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER) {\n \t\t\tif (!vxlan_vnifilter_lookup(tmp, vni))\n \t\t\t\tcontinue;\n-\t\t} else if (tmp-\u003ecfg.vni != vni) {\n+\t\t} else if (tmp_cfg-\u003evni != vni) {\n \t\t\tcontinue;\n \t\t}\n-\t\tif (tmp-\u003ecfg.dst_port != conf-\u003edst_port)\n+\t\tif (tmp_cfg-\u003edst_port != conf-\u003edst_port)\n \t\t\tcontinue;\n-\t\tif ((tmp-\u003ecfg.flags \u0026 (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) !=\n+\t\tif ((tmp_cfg-\u003eflags \u0026 (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) !=\n \t\t    (conf-\u003eflags \u0026 (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)))\n \t\t\tcontinue;\n \n \t\tif ((conf-\u003eflags \u0026 VXLAN_F_IPV6_LINKLOCAL) \u0026\u0026\n-\t\t    tmp-\u003ecfg.remote_ifindex != conf-\u003eremote_ifindex)\n+\t\t    tmp_cfg-\u003eremote_ifindex != conf-\u003eremote_ifindex)\n \t\t\tcontinue;\n \n \t\treturn -EEXIST;\n@@ -3884,7 +3970,7 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf,\n }\n \n static void vxlan_config_apply(struct net_device *dev,\n-\t\t\t       struct vxlan_config *conf,\n+\t\t\t       struct vxlan_config *new_cfg,\n \t\t\t       struct net_device *lowerdev,\n \t\t\t       struct net *src_net,\n \t\t\t       bool changelink)\n@@ -3892,8 +3978,9 @@ static void vxlan_config_apply(struct net_device *dev,\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n \tunsigned short needed_headroom = ETH_HLEN;\n+\tstruct vxlan_config *old_cfg;\n \tint max_mtu = ETH_MAX_MTU;\n-\tu32 flags = conf-\u003eflags;\n+\tu32 flags = new_cfg-\u003eflags;\n \n \tif (!changelink) {\n \t\tif (flags \u0026 VXLAN_F_GPE)\n@@ -3901,18 +3988,18 @@ static void vxlan_config_apply(struct net_device *dev,\n \t\telse\n \t\t\tvxlan_ether_setup(dev);\n \n-\t\tif (conf-\u003emtu)\n-\t\t\tdev-\u003emtu = conf-\u003emtu;\n+\t\tif (new_cfg-\u003emtu)\n+\t\t\tdev-\u003emtu = new_cfg-\u003emtu;\n \n \t\tvxlan-\u003enet = src_net;\n \t}\n \n-\tdst-\u003eremote_vni = conf-\u003evni;\n+\tdst-\u003eremote_vni = new_cfg-\u003evni;\n \n-\tmemcpy(\u0026dst-\u003eremote_ip, \u0026conf-\u003eremote_ip, sizeof(conf-\u003eremote_ip));\n+\tmemcpy(\u0026dst-\u003eremote_ip, \u0026new_cfg-\u003eremote_ip, sizeof(new_cfg-\u003eremote_ip));\n \n \tif (lowerdev) {\n-\t\tdst-\u003eremote_ifindex = conf-\u003eremote_ifindex;\n+\t\tdst-\u003eremote_ifindex = new_cfg-\u003eremote_ifindex;\n \n \t\tnetif_inherit_tso_max(dev, lowerdev);\n \n@@ -3925,7 +4012,7 @@ static void vxlan_config_apply(struct net_device *dev,\n \t\tif (max_mtu \u003c ETH_MIN_MTU)\n \t\t\tmax_mtu = ETH_MIN_MTU;\n \n-\t\tif (!changelink \u0026\u0026 !conf-\u003emtu)\n+\t\tif (!changelink \u0026\u0026 !new_cfg-\u003emtu)\n \t\t\tdev-\u003emtu = max_mtu;\n \t}\n \n@@ -3937,7 +4024,10 @@ static void vxlan_config_apply(struct net_device *dev,\n \tneeded_headroom += vxlan_headroom(flags);\n \tdev-\u003eneeded_headroom = needed_headroom;\n \n-\tmemcpy(\u0026vxlan-\u003ecfg, conf, sizeof(*conf));\n+\told_cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\trcu_assign_pointer(vxlan-\u003ecfg, new_cfg);\n+\tif (old_cfg)\n+\t\tkfree_rcu(old_cfg, rcu);\n }\n \n static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,\n@@ -3946,13 +4036,18 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct net_device *lowerdev;\n+\tstruct vxlan_config *new_cfg;\n \tint ret;\n \n \tret = vxlan_config_validate(src_net, conf, \u0026lowerdev, vxlan, extack);\n \tif (ret)\n \t\treturn ret;\n \n-\tvxlan_config_apply(dev, conf, lowerdev, src_net, false);\n+\tnew_cfg = kmemdup(conf, sizeof(*conf), GFP_KERNEL);\n+\tif (!new_cfg)\n+\t\treturn -ENOMEM;\n+\n+\tvxlan_config_apply(dev, new_cfg, lowerdev, src_net, false);\n \n \treturn 0;\n }\n@@ -3964,6 +4059,7 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,\n \tstruct vxlan_net *vn = net_generic(net, vxlan_net_id);\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct net_device *remote_dev = NULL;\n+\tconst struct vxlan_config *cfg;\n \tstruct vxlan_rdst *dst;\n \tint err;\n \n@@ -3972,6 +4068,8 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,\n \tif (err)\n \t\treturn err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n \tdev-\u003eethtool_ops = \u0026vxlan_ethtool_ops;\n \n \terr = register_netdevice(dev);\n@@ -4003,7 +4101,7 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,\n \t\t\t\t       \u0026dst-\u003eremote_ip,\n \t\t\t\t       NUD_REACHABLE | NUD_PERMANENT,\n \t\t\t\t       NLM_F_EXCL | NLM_F_CREATE,\n-\t\t\t\t       vxlan-\u003ecfg.dst_port,\n+\t\t\t\t       cfg-\u003edst_port,\n \t\t\t\t       dst-\u003eremote_vni,\n \t\t\t\t       dst-\u003eremote_vni,\n \t\t\t\t       dst-\u003eremote_ifindex,\n@@ -4067,8 +4165,12 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[],\n \tmemset(conf, 0, sizeof(*conf));\n \n \t/* if changelink operation, start with old existing cfg */\n-\tif (changelink)\n-\t\tmemcpy(conf, \u0026vxlan-\u003ecfg, sizeof(*conf));\n+\tif (changelink) {\n+\t\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n+\t\tif (cfg)\n+\t\t\tmemcpy(conf, cfg, sizeof(*conf));\n+\t}\n \n \tif (data[IFLA_VXLAN_ID]) {\n \t\t__be32 vni = cpu_to_be32(nla_get_u32(data[IFLA_VXLAN_ID]));\n@@ -4415,9 +4517,11 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],\n \t\t\t    struct netlink_ext_ack *extack)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tbool rem_ip_changed, change_igmp;\n \tstruct net_device *lowerdev;\n \tstruct vxlan_config conf;\n+\tstruct vxlan_config *new_cfg;\n \tstruct vxlan_rdst *dst;\n \tint err;\n \n@@ -4431,13 +4535,19 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],\n \tif (err)\n \t\treturn err;\n \n+\tnew_cfg = kmemdup(\u0026conf, sizeof(conf), GFP_KERNEL);\n+\tif (!new_cfg)\n+\t\treturn -ENOMEM;\n+\n \tif (dst-\u003eremote_dev == lowerdev)\n \t\tlowerdev = NULL;\n \n \terr = netdev_adjacent_change_prepare(dst-\u003eremote_dev, lowerdev, dev,\n \t\t\t\t\t     extack);\n-\tif (err)\n+\tif (err) {\n+\t\tkfree(new_cfg);\n \t\treturn err;\n+\t}\n \n \trem_ip_changed = !vxlan_addr_equal(\u0026conf.remote_ip, \u0026dst-\u003eremote_ip);\n \tchange_igmp = vxlan-\u003edev-\u003eflags \u0026 IFF_UP \u0026\u0026\n@@ -4452,7 +4562,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],\n \t\t\t\t\t       \u0026conf.remote_ip,\n \t\t\t\t\t       NUD_REACHABLE | NUD_PERMANENT,\n \t\t\t\t\t       NLM_F_APPEND | NLM_F_CREATE,\n-\t\t\t\t\t       vxlan-\u003ecfg.dst_port,\n+\t\t\t\t\t       cfg-\u003edst_port,\n \t\t\t\t\t       conf.vni, conf.vni,\n \t\t\t\t\t       conf.remote_ifindex,\n \t\t\t\t\t       NTF_SELF, 0, true, extack);\n@@ -4460,13 +4570,14 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],\n \t\t\t\tspin_unlock_bh(\u0026vxlan-\u003ehash_lock);\n \t\t\t\tnetdev_adjacent_change_abort(dst-\u003eremote_dev,\n \t\t\t\t\t\t\t     lowerdev, dev);\n+\t\t\t\tkfree(new_cfg);\n \t\t\t\treturn err;\n \t\t\t}\n \t\t}\n \t\tif (!vxlan_addr_any(\u0026dst-\u003eremote_ip))\n \t\t\t__vxlan_fdb_delete(vxlan, all_zeros_mac,\n \t\t\t\t\t   dst-\u003eremote_ip,\n-\t\t\t\t\t   vxlan-\u003ecfg.dst_port,\n+\t\t\t\t\t   cfg-\u003edst_port,\n \t\t\t\t\t   dst-\u003eremote_vni,\n \t\t\t\t\t   dst-\u003eremote_vni,\n \t\t\t\t\t   dst-\u003eremote_ifindex,\n@@ -4476,12 +4587,13 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],\n \t\t/* If vni filtering device, also update fdb entries of\n \t\t * all vnis that were using default remote ip\n \t\t */\n-\t\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER) {\n+\t\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER) {\n \t\t\terr = vxlan_vnilist_update_group(vxlan, \u0026dst-\u003eremote_ip,\n \t\t\t\t\t\t\t \u0026conf.remote_ip, extack);\n \t\t\tif (err) {\n \t\t\t\tnetdev_adjacent_change_abort(dst-\u003eremote_dev,\n \t\t\t\t\t\t\t     lowerdev, dev);\n+\t\t\t\tkfree(new_cfg);\n \t\t\t\treturn err;\n \t\t\t}\n \t\t}\n@@ -4490,13 +4602,13 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],\n \tif (change_igmp \u0026\u0026 vxlan_addr_multicast(\u0026dst-\u003eremote_ip))\n \t\terr = vxlan_multicast_leave(vxlan);\n \n-\tif (conf.age_interval != vxlan-\u003ecfg.age_interval)\n+\tif (conf.age_interval != cfg-\u003eage_interval)\n \t\tmod_timer(\u0026vxlan-\u003eage_timer, jiffies);\n \n \tnetdev_adjacent_change_commit(dst-\u003eremote_dev, lowerdev, dev);\n \tif (lowerdev \u0026\u0026 lowerdev != dst-\u003eremote_dev)\n \t\tdst-\u003eremote_dev = lowerdev;\n-\tvxlan_config_apply(dev, \u0026conf, lowerdev, vxlan-\u003enet, true);\n+\tvxlan_config_apply(dev, new_cfg, lowerdev, vxlan-\u003enet, true);\n \n \tif (!err \u0026\u0026 change_igmp \u0026\u0026\n \t    vxlan_addr_multicast(\u0026dst-\u003eremote_ip))\n@@ -4559,11 +4671,22 @@ static size_t vxlan_get_size(const struct net_device *dev)\n static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)\n {\n \tconst struct vxlan_dev *vxlan = netdev_priv(dev);\n-\tconst struct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n-\tstruct ifla_vxlan_port_range ports = {\n-\t\t.low =  htons(vxlan-\u003ecfg.port_min),\n-\t\t.high = htons(vxlan-\u003ecfg.port_max),\n-\t};\n+\tstruct ifla_vxlan_port_range ports;\n+\tconst struct vxlan_config *cfg;\n+\tconst struct vxlan_rdst *dst;\n+\tint err = 0;\n+\n+\tdst = \u0026vxlan-\u003edefault_dst;\n+\n+\trcu_read_lock();\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\tif (!cfg) {\n+\t\terr = -ENODEV;\n+\t\tgoto out;\n+\t}\n+\n+\tports.low = htons(cfg-\u003eport_min);\n+\tports.high = htons(cfg-\u003eport_max);\n \n \tif (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst-\u003eremote_vni)))\n \t\tgoto nla_put_failure;\n@@ -4585,84 +4708,87 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)\n \tif (dst-\u003eremote_ifindex \u0026\u0026 nla_put_u32(skb, IFLA_VXLAN_LINK, dst-\u003eremote_ifindex))\n \t\tgoto nla_put_failure;\n \n-\tif (!vxlan_addr_any(\u0026vxlan-\u003ecfg.saddr)) {\n-\t\tif (vxlan-\u003ecfg.saddr.sa.sa_family == AF_INET) {\n+\tif (!vxlan_addr_any(\u0026cfg-\u003esaddr)) {\n+\t\tif (cfg-\u003esaddr.sa.sa_family == AF_INET) {\n \t\t\tif (nla_put_in_addr(skb, IFLA_VXLAN_LOCAL,\n-\t\t\t\t\t    vxlan-\u003ecfg.saddr.sin.sin_addr.s_addr))\n+\t\t\t\t\t    cfg-\u003esaddr.sin.sin_addr.s_addr))\n \t\t\t\tgoto nla_put_failure;\n #if IS_ENABLED(CONFIG_IPV6)\n \t\t} else {\n \t\t\tif (nla_put_in6_addr(skb, IFLA_VXLAN_LOCAL6,\n-\t\t\t\t\t     \u0026vxlan-\u003ecfg.saddr.sin6.sin6_addr))\n+\t\t\t\t\t     \u0026cfg-\u003esaddr.sin6.sin6_addr))\n \t\t\t\tgoto nla_put_failure;\n #endif\n \t\t}\n \t}\n \n-\tif (nla_put_u8(skb, IFLA_VXLAN_TTL, vxlan-\u003ecfg.ttl) ||\n+\tif (nla_put_u8(skb, IFLA_VXLAN_TTL, cfg-\u003ettl) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_TTL_INHERIT,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_TTL_INHERIT)) ||\n-\t    nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan-\u003ecfg.tos) ||\n-\t    nla_put_u8(skb, IFLA_VXLAN_DF, vxlan-\u003ecfg.df) ||\n-\t    nla_put_be32(skb, IFLA_VXLAN_LABEL, vxlan-\u003ecfg.label) ||\n-\t    nla_put_u32(skb, IFLA_VXLAN_LABEL_POLICY, vxlan-\u003ecfg.label_policy) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_TTL_INHERIT)) ||\n+\t    nla_put_u8(skb, IFLA_VXLAN_TOS, cfg-\u003etos) ||\n+\t    nla_put_u8(skb, IFLA_VXLAN_DF, cfg-\u003edf) ||\n+\t    nla_put_be32(skb, IFLA_VXLAN_LABEL, cfg-\u003elabel) ||\n+\t    nla_put_u32(skb, IFLA_VXLAN_LABEL_POLICY, cfg-\u003elabel_policy) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_LEARNING,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_LEARN)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_LEARN)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_PROXY,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_PROXY)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_PROXY)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_RSC,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_RSC)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_RSC)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_L2MISS,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_L2MISS)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_L2MISS)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_L3MISS,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_L3MISS)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_COLLECT_METADATA,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA)) ||\n-\t    nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan-\u003ecfg.age_interval) ||\n-\t    nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan-\u003ecfg.addrmax) ||\n-\t    nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan-\u003ecfg.dst_port) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA)) ||\n+\t    nla_put_u32(skb, IFLA_VXLAN_AGEING, cfg-\u003eage_interval) ||\n+\t    nla_put_u32(skb, IFLA_VXLAN_LIMIT, cfg-\u003eaddrmax) ||\n+\t    nla_put_be16(skb, IFLA_VXLAN_PORT, cfg-\u003edst_port) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_UDP_CSUM,\n-\t\t       !(vxlan-\u003ecfg.flags \u0026 VXLAN_F_UDP_ZERO_CSUM_TX)) ||\n+\t\t       !(cfg-\u003eflags \u0026 VXLAN_F_UDP_ZERO_CSUM_TX)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_UDP_ZERO_CSUM6_TX)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_UDP_ZERO_CSUM6_TX)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_UDP_ZERO_CSUM6_RX)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_UDP_ZERO_CSUM6_RX)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_REMCSUM_TX,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_REMCSUM_TX)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_REMCSUM_TX)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_REMCSUM_RX,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_REMCSUM_RX)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_REMCSUM_RX)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_LOCALBYPASS,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_LOCALBYPASS)))\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_LOCALBYPASS)))\n \t\tgoto nla_put_failure;\n \n \tif (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), \u0026ports))\n \t\tgoto nla_put_failure;\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_GBP \u0026\u0026\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_GBP \u0026\u0026\n \t    nla_put_flag(skb, IFLA_VXLAN_GBP))\n \t\tgoto nla_put_failure;\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_GPE \u0026\u0026\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_GPE \u0026\u0026\n \t    nla_put_flag(skb, IFLA_VXLAN_GPE))\n \t\tgoto nla_put_failure;\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_REMCSUM_NOPARTIAL \u0026\u0026\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_REMCSUM_NOPARTIAL \u0026\u0026\n \t    nla_put_flag(skb, IFLA_VXLAN_REMCSUM_NOPARTIAL))\n \t\tgoto nla_put_failure;\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER \u0026\u0026\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER \u0026\u0026\n \t    nla_put_u8(skb, IFLA_VXLAN_VNIFILTER,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)))\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER)))\n \t\tgoto nla_put_failure;\n \n \tif (nla_put(skb, IFLA_VXLAN_RESERVED_BITS,\n-\t\t    sizeof(vxlan-\u003ecfg.reserved_bits),\n-\t\t    \u0026vxlan-\u003ecfg.reserved_bits))\n+\t\t    sizeof(cfg-\u003ereserved_bits),\n+\t\t    \u0026cfg-\u003ereserved_bits))\n \t\tgoto nla_put_failure;\n \n-\treturn 0;\n+out:\n+\trcu_read_unlock();\n+\treturn err;\n \n nla_put_failure:\n+\trcu_read_unlock();\n \treturn -EMSGSIZE;\n }\n \ndiff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c\nindex 055a4969f593c9..eb15bb052a6a87 100644\n--- a/drivers/net/vxlan/vxlan_mdb.c\n+++ b/drivers/net/vxlan/vxlan_mdb.c\n@@ -164,6 +164,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,\n \t\t\t\t     const struct vxlan_mdb_entry *mdb_entry,\n \t\t\t\t     const struct vxlan_mdb_remote *remote)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct vxlan_rdst *rd = rtnl_dereference(remote-\u003erd);\n \tstruct br_mdb_entry e;\n \tstruct nlattr *nest;\n@@ -188,7 +189,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,\n \t    vxlan_nla_put_addr(skb, MDBA_MDB_EATTR_DST, \u0026rd-\u003eremote_ip))\n \t\tgoto nest_err;\n \n-\tif (rd-\u003eremote_port \u0026\u0026 rd-\u003eremote_port != vxlan-\u003ecfg.dst_port \u0026\u0026\n+\tif (rd-\u003eremote_port \u0026\u0026 rd-\u003eremote_port != cfg-\u003edst_port \u0026\u0026\n \t    nla_put_u16(skb, MDBA_MDB_EATTR_DST_PORT,\n \t\t\tbe16_to_cpu(rd-\u003eremote_port)))\n \t\tgoto nest_err;\n@@ -201,7 +202,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,\n \t    nla_put_u32(skb, MDBA_MDB_EATTR_IFINDEX, rd-\u003eremote_ifindex))\n \t\tgoto nest_err;\n \n-\tif ((vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026\n+\tif ((cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026\n \t    mdb_entry-\u003ekey.vni \u0026\u0026 nla_put_u32(skb, MDBA_MDB_EATTR_SRC_VNI,\n \t\t\t\t\t      be32_to_cpu(mdb_entry-\u003ekey.vni)))\n \t\tgoto nest_err;\n@@ -604,6 +605,9 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,\n {\n \tstruct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]);\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *vcfg;\n+\n+\tvcfg = rtnl_dereference(vxlan-\u003ecfg);\n \n \tmemset(cfg, 0, sizeof(*cfg));\n \tcfg-\u003evxlan = vxlan;\n@@ -613,7 +617,7 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,\n \tcfg-\u003efilter_mode = MCAST_EXCLUDE;\n \tcfg-\u003ert_protocol = RTPROT_STATIC;\n \tcfg-\u003eremote_vni = vxlan-\u003edefault_dst.remote_vni;\n-\tcfg-\u003eremote_port = vxlan-\u003ecfg.dst_port;\n+\tcfg-\u003eremote_port = vcfg-\u003edst_port;\n \n \tif (entry-\u003eifindex != dev-\u003eifindex) {\n \t\tNL_SET_ERR_MSG_MOD(extack, \"Port net device must be the VXLAN net device\");\n@@ -938,11 +942,12 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,\n \t\t\t    const struct vxlan_mdb_entry *mdb_entry,\n \t\t\t    const struct vxlan_mdb_remote *remote)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tconst struct vxlan_mdb_entry_key *group = \u0026mdb_entry-\u003ekey;\n \tstruct vxlan_rdst *rd = rtnl_dereference(remote-\u003erd);\n \tsize_t nlmsg_size;\n \n-\t\t     /* MDBA_MDB_ENTRY_INFO */\n+\t/* MDBA_MDB_ENTRY_INFO */\n \tnlmsg_size = nla_total_size(sizeof(struct br_mdb_entry)) +\n \t\t     /* MDBA_MDB_EATTR_TIMER */\n \t\t     nla_total_size(sizeof(u32));\n@@ -959,7 +964,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,\n \t/* MDBA_MDB_EATTR_DST */\n \tnlmsg_size += nla_total_size(vxlan_addr_size(\u0026rd-\u003eremote_ip));\n \t/* MDBA_MDB_EATTR_DST_PORT */\n-\tif (rd-\u003eremote_port \u0026\u0026 rd-\u003eremote_port != vxlan-\u003ecfg.dst_port)\n+\tif (rd-\u003eremote_port \u0026\u0026 rd-\u003eremote_port != cfg-\u003edst_port)\n \t\tnlmsg_size += nla_total_size(sizeof(u16));\n \t/* MDBA_MDB_EATTR_VNI */\n \tif (rd-\u003eremote_vni != vxlan-\u003edefault_dst.remote_vni)\n@@ -968,7 +973,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,\n \tif (rd-\u003eremote_ifindex)\n \t\tnlmsg_size += nla_total_size(sizeof(u32));\n \t/* MDBA_MDB_EATTR_SRC_VNI */\n-\tif ((vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026 group-\u003evni)\n+\tif ((cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026 group-\u003evni)\n \t\tnlmsg_size += nla_total_size(sizeof(u32));\n \n \treturn nlmsg_size;\n@@ -1202,7 +1207,7 @@ vxlan_mdb_entry_get(struct vxlan_dev *vxlan,\n \t\tgoto err_free_entry;\n \n \tif (hlist_is_singular_node(\u0026mdb_entry-\u003emdb_node, \u0026vxlan-\u003emdb_list))\n-\t\tvxlan-\u003ecfg.flags |= VXLAN_F_MDB;\n+\t\trtnl_dereference(vxlan-\u003ecfg)-\u003eflags |= VXLAN_F_MDB;\n \n \treturn mdb_entry;\n \n@@ -1219,7 +1224,7 @@ static void vxlan_mdb_entry_put(struct vxlan_dev *vxlan,\n \t\treturn;\n \n \tif (hlist_is_singular_node(\u0026mdb_entry-\u003emdb_node, \u0026vxlan-\u003emdb_list))\n-\t\tvxlan-\u003ecfg.flags \u0026= ~VXLAN_F_MDB;\n+\t\trtnl_dereference(vxlan-\u003ecfg)-\u003eflags \u0026= ~VXLAN_F_MDB;\n \n \trhashtable_remove_fast(\u0026vxlan-\u003emdb_tbl, \u0026mdb_entry-\u003erhnode,\n \t\t\t       vxlan_mdb_rht_params);\n@@ -1607,6 +1612,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,\n \t\t\t\t\t\tstruct sk_buff *skb,\n \t\t\t\t\t\t__be32 src_vni)\n {\n+\tconst struct vxlan_config *cfg;\n \tstruct vxlan_mdb_entry *mdb_entry;\n \tstruct vxlan_mdb_entry_key group;\n \n@@ -1614,10 +1620,14 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,\n \t    is_broadcast_ether_addr(eth_hdr(skb)-\u003eh_dest))\n \t\treturn NULL;\n \n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\tif (!cfg)\n+\t\treturn NULL;\n+\n \t/* When not in collect metadata mode, 'src_vni' is zero, but MDB\n \t * entries are stored with the VNI of the VXLAN device.\n \t */\n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA))\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA))\n \t\tsrc_vni = vxlan-\u003edefault_dst.remote_vni;\n \n \tmemset(\u0026group, 0, sizeof(group));\n@@ -1687,8 +1697,11 @@ netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,\n \t\t\t   struct sk_buff *skb)\n {\n \tstruct vxlan_mdb_remote *remote, *fremote = NULL;\n+\tconst struct vxlan_config *cfg;\n \t__be32 src_vni = mdb_entry-\u003ekey.vni;\n \n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\n \tlist_for_each_entry_rcu(remote, \u0026mdb_entry-\u003eremotes, list) {\n \t\tstruct sk_buff *skb1;\n \n@@ -1704,12 +1717,12 @@ netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,\n \n \t\tskb1 = skb_clone(skb, GFP_ATOMIC);\n \t\tif (skb1)\n-\t\t\tvxlan_xmit_one(skb1, vxlan-\u003edev, src_vni,\n+\t\t\tvxlan_xmit_one(skb1, vxlan-\u003edev, cfg, src_vni,\n \t\t\t\t       rcu_dereference(remote-\u003erd), false);\n \t}\n \n \tif (fremote)\n-\t\tvxlan_xmit_one(skb, vxlan-\u003edev, src_vni,\n+\t\tvxlan_xmit_one(skb, vxlan-\u003edev, cfg, src_vni,\n \t\t\t       rcu_dereference(fremote-\u003erd), false);\n \telse\n \t\tkfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);\n@@ -1740,7 +1753,7 @@ void vxlan_mdb_fini(struct vxlan_dev *vxlan)\n \tstruct vxlan_mdb_flush_desc desc = {};\n \n \tvxlan_mdb_flush(vxlan, \u0026desc);\n-\tWARN_ON_ONCE(vxlan-\u003ecfg.flags \u0026 VXLAN_F_MDB);\n+\tWARN_ON_ONCE(rtnl_dereference(vxlan-\u003ecfg)-\u003eflags \u0026 VXLAN_F_MDB);\n \trhashtable_free_and_destroy(\u0026vxlan-\u003emdb_tbl, vxlan_mdb_check_empty,\n \t\t\t\t    NULL);\n }\ndiff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c\nindex 3b75b48dc726df..e2cf10da274f1b 100644\n--- a/drivers/net/vxlan/vxlan_multicast.c\n+++ b/drivers/net/vxlan/vxlan_multicast.c\n@@ -147,6 +147,8 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,\n #endif\n \n \tlist_for_each_entry(vxlan, \u0026vn-\u003evxlan_list, next) {\n+\t\tconst struct vxlan_config *cfg;\n+\n \t\tif (!netif_running(vxlan-\u003edev) || vxlan == dev)\n \t\t\tcontinue;\n \n@@ -158,7 +160,9 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,\n \t\t    rtnl_dereference(vxlan-\u003evn6_sock) != sock6)\n \t\t\tcontinue;\n #endif\n-\t\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER) {\n+\t\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n+\t\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER) {\n \t\t\tif (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex))\n \t\t\t\tcontinue;\n \t\t} else {\n@@ -233,6 +237,7 @@ static int vxlan_multicast_leave_vnigrp(struct vxlan_dev *vxlan)\n \n int vxlan_multicast_join(struct vxlan_dev *vxlan)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tint ret = 0;\n \n \tif (vxlan_addr_multicast(\u0026vxlan-\u003edefault_dst.remote_ip)) {\n@@ -244,7 +249,7 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan)\n \t\t\treturn ret;\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER)\n \t\treturn vxlan_multicast_join_vnigrp(vxlan);\n \n \treturn 0;\n@@ -252,6 +257,7 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan)\n \n int vxlan_multicast_leave(struct vxlan_dev *vxlan)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct vxlan_net *vn = net_generic(vxlan-\u003enet, vxlan_net_id);\n \tint ret = 0;\n \n@@ -263,7 +269,7 @@ int vxlan_multicast_leave(struct vxlan_dev *vxlan)\n \t\t\treturn ret;\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER)\n \t\treturn vxlan_multicast_leave_vnigrp(vxlan);\n \n \treturn 0;\ndiff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h\nindex b1eec221636088..3d5f21c11ab4ff 100644\n--- a/drivers/net/vxlan/vxlan_private.h\n+++ b/drivers/net/vxlan/vxlan_private.h\n@@ -195,9 +195,10 @@ int vxlan_fdb_update(struct vxlan_dev *vxlan,\n \t\t     __u32 ifindex, __u16 ndm_flags, u32 nhid,\n \t\t     bool swdev_notify, struct netlink_ext_ack *extack);\n void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n+\t\t    const struct vxlan_config *cfg,\n \t\t    __be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc);\n int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan,\n-\t\t     struct vxlan_config *conf, __be32 vni);\n+\t\t     const struct vxlan_config *conf, __be32 vni);\n \n /* vxlan_vnifilter.c */\n int vxlan_vnigroup_init(struct vxlan_dev *vxlan);\n@@ -205,7 +206,8 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan);\n \n int vxlan_vnifilter_init(void);\n void vxlan_vnifilter_uninit(void);\n-void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni,\n+void vxlan_vnifilter_count(struct vxlan_dev *vxlan,\n+\t\t\t   const struct vxlan_config *cfg, __be32 vni,\n \t\t\t   struct vxlan_vni_node *vninode,\n \t\t\t   int type, unsigned int len);\n \ndiff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c\nindex 3e76f4e210944f..f3dda671b4e456 100644\n--- a/drivers/net/vxlan/vxlan_vnifilter.c\n+++ b/drivers/net/vxlan/vxlan_vnifilter.c\n@@ -171,13 +171,14 @@ static void vxlan_vnifilter_stats_add(struct vxlan_vni_node *vninode,\n \tu64_stats_update_end(\u0026pstats-\u003esyncp);\n }\n \n-void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni,\n+void vxlan_vnifilter_count(struct vxlan_dev *vxlan,\n+\t\t\t   const struct vxlan_config *cfg, __be32 vni,\n \t\t\t   struct vxlan_vni_node *vninode,\n \t\t\t   int type, unsigned int len)\n {\n \tstruct vxlan_vni_node *vnode;\n \n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER))\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER))\n \t\treturn;\n \n \tif (vninode) {\n@@ -336,13 +337,15 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev,\n \tstruct vxlan_vni_node *tmp, *v, *vbegin = NULL, *vend = NULL;\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct tunnel_msg *new_tmsg, *tmsg;\n+\tconst struct vxlan_config *cfg;\n \tint idx = 0, s_idx = cb-\u003eargs[1];\n \tstruct vxlan_vni_group *vg;\n \tstruct nlmsghdr *nlh;\n \tbool dump_stats;\n \tint err = 0;\n \n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER))\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER))\n \t\treturn -EINVAL;\n \n \t/* RCU needed because of the vni locking rules (rcu || rtnl) */\n@@ -477,6 +480,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,\n \t\t\t\t\t  union vxlan_addr *remote_ip,\n \t\t\t\t\t  struct netlink_ext_ack *extack)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n \tint err = 0;\n \n@@ -486,7 +490,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,\n \t\t\t\t       remote_ip,\n \t\t\t\t       NUD_REACHABLE | NUD_PERMANENT,\n \t\t\t\t       NLM_F_APPEND | NLM_F_CREATE,\n-\t\t\t\t       vxlan-\u003ecfg.dst_port,\n+\t\t\t\t       cfg-\u003edst_port,\n \t\t\t\t       vni,\n \t\t\t\t       vni,\n \t\t\t\t       dst-\u003eremote_ifindex,\n@@ -500,7 +504,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,\n \tif (old_remote_ip \u0026\u0026 !vxlan_addr_any(old_remote_ip)) {\n \t\t__vxlan_fdb_delete(vxlan, all_zeros_mac,\n \t\t\t\t   *old_remote_ip,\n-\t\t\t\t   vxlan-\u003ecfg.dst_port,\n+\t\t\t\t   cfg-\u003edst_port,\n \t\t\t\t   vni, vni,\n \t\t\t\t   dst-\u003eremote_ifindex,\n \t\t\t\t   true);\n@@ -614,6 +618,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,\n \t\t\t\t   struct vxlan_vni_node *vninode)\n {\n \tstruct vxlan_net *vn = net_generic(vxlan-\u003enet, vxlan_net_id);\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n \n \t/* if per vni remote_ip not present, delete the\n@@ -625,7 +630,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,\n \t\t__vxlan_fdb_delete(vxlan, all_zeros_mac,\n \t\t\t\t   (vxlan_addr_any(\u0026vninode-\u003eremote_ip) ?\n \t\t\t\t   dst-\u003eremote_ip : vninode-\u003eremote_ip),\n-\t\t\t\t   vxlan-\u003ecfg.dst_port,\n+\t\t\t\t   cfg-\u003edst_port,\n \t\t\t\t   vninode-\u003evni, vninode-\u003evni,\n \t\t\t\t   dst-\u003eremote_ifindex,\n \t\t\t\t   true);\n@@ -725,6 +730,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan,\n \t\t\t u32 vni, union vxlan_addr *group,\n \t\t\t struct netlink_ext_ack *extack)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct vxlan_vni_node *vninode;\n \t__be32 v = cpu_to_be32(vni);\n \tbool changed = false;\n@@ -733,7 +739,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan,\n \tif (vxlan_vnifilter_lookup(vxlan, v))\n \t\treturn vxlan_vni_update(vxlan, vg, v, group, \u0026changed, extack);\n \n-\terr = vxlan_vni_in_use(vxlan-\u003enet, vxlan, \u0026vxlan-\u003ecfg, v);\n+\terr = vxlan_vni_in_use(vxlan-\u003enet, vxlan, cfg, v);\n \tif (err) {\n \t\tNL_SET_ERR_MSG(extack, \"VNI in use\");\n \t\treturn err;\n@@ -942,6 +948,7 @@ static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh,\n \t\t\t\t   struct netlink_ext_ack *extack)\n {\n \tstruct net *net = sock_net(skb-\u003esk);\n+\tconst struct vxlan_config *cfg;\n \tstruct tunnel_msg *tmsg;\n \tstruct vxlan_dev *vxlan;\n \tstruct net_device *dev;\n@@ -966,8 +973,9 @@ static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh,\n \t}\n \n \tvxlan = netdev_priv(dev);\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n \n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER))\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER))\n \t\treturn -EOPNOTSUPP;\n \n \tnlmsg_for_each_attr_type(attr, VXLAN_VNIFILTER_ENTRY, nlh,\ndiff --git a/include/net/vxlan.h b/include/net/vxlan.h\nindex dfba89695efcfa..7321768b0f2c1e 100644\n--- a/include/net/vxlan.h\n+++ b/include/net/vxlan.h\n@@ -229,6 +229,7 @@ struct vxlan_config {\n \tbool\t\t\t\tno_share;\n \tenum ifla_vxlan_df\t\tdf;\n \tstruct vxlanhdr\t\t\treserved_bits;\n+\tstruct rcu_head\t\t\trcu;\n };\n \n enum {\n@@ -301,7 +302,7 @@ struct vxlan_dev {\n \tunsigned int\t  addrcnt;\n \tstruct gro_cells  gro_cells;\n \n-\tstruct vxlan_config\tcfg;\n+\tstruct vxlan_config __rcu\t*cfg;\n \n \tstruct vxlan_vni_group  __rcu *vnigrp;\n \ndiff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c\nindex c1b37b50d29e15..9b1101f6ce1e1c 100644\n--- a/net/openvswitch/vport-vxlan.c\n+++ b/net/openvswitch/vport-vxlan.c\n@@ -22,26 +22,40 @@ static struct vport_ops ovs_vxlan_netdev_vport_ops;\n static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(vport-\u003edev);\n-\t__be16 dst_port = vxlan-\u003ecfg.dst_port;\n+\tconst struct vxlan_config *cfg;\n+\t__be16 dst_port;\n+\tint err = 0;\n \n-\tif (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(dst_port)))\n-\t\treturn -EMSGSIZE;\n+\trcu_read_lock();\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\tdst_port = cfg-\u003edst_port;\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_GBP) {\n+\tif (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(dst_port))) {\n+\t\terr = -EMSGSIZE;\n+\t\tgoto out;\n+\t}\n+\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_GBP) {\n \t\tstruct nlattr *exts;\n \n \t\texts = nla_nest_start_noflag(skb, OVS_TUNNEL_ATTR_EXTENSION);\n-\t\tif (!exts)\n-\t\t\treturn -EMSGSIZE;\n+\t\tif (!exts) {\n+\t\t\terr = -EMSGSIZE;\n+\t\t\tgoto out;\n+\t\t}\n \n-\t\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_GBP \u0026\u0026\n-\t\t    nla_put_flag(skb, OVS_VXLAN_EXT_GBP))\n-\t\t\treturn -EMSGSIZE;\n+\t\tif (cfg-\u003eflags \u0026 VXLAN_F_GBP \u0026\u0026\n+\t\t    nla_put_flag(skb, OVS_VXLAN_EXT_GBP)) {\n+\t\t\terr = -EMSGSIZE;\n+\t\t\tgoto out;\n+\t\t}\n \n \t\tnla_nest_end(skb, exts);\n \t}\n \n-\treturn 0;\n+out:\n+\trcu_read_unlock();\n+\treturn err;\n }\n \n static const struct nla_policy exts_policy[OVS_VXLAN_EXT_MAX + 1] = {\n"
}

2/1 2026/07/09 09:54 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "VXLAN",
    "MLXSW_SPECTRUM",
    "OPENVSWITCH_VXLAN"
  ],
  "FocusSymbols": [
    "vxlan_changelink",
    "vxlan_dev_configure",
    "vxlan_fdb_create",
    "vxlan_nl2conf",
    "vxlan_fill_info",
    "vxlan_mdb_config_init",
    "vxlan_vni_add",
    "vxlan_vnifilter_process",
    "vxlan_get_options",
    "mlxsw_sp_nve_vxlan_config"
  ],
  "Reasoning": "The patch changes how the VXLAN configuration (`vxlan-\u003ecfg`) is accessed, moving it from an embedded struct to an RCU-protected pointer. This is a significant functional change in the core VXLAN driver, affecting device creation, configuration, FDB, MDB, VNI filtering, and integration with other subsystems like Open vSwitch and Mellanox Spectrum switchdev. It introduces new memory allocations and RCU dereferences, which should be fuzzed to ensure no use-after-free or memory leaks occur.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 0975054fe9075c08bc58349b1bd8d7c10d8bc26d
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Jul 9 09:54:56 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
index 52c2fe3644d4b9..50cea39323f570 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 fe45e533a4b2ef..f25105ceb3f1bf 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 67c367cc566233..86b853d7739639 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;
@@ -377,14 +381,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 +398,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 +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, mac, vni);
+	f = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan->cfg), mac, vni);
 	rcu_read_unlock();
 
 	return f;
@@ -457,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, 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) {
@@ -863,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);
@@ -1148,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;
 
@@ -1164,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);
@@ -1184,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]) {
@@ -1194,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]) {
@@ -1204,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]) {
@@ -1394,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, addr, vni);
+	f = vxlan_find_mac_rcu(vxlan, cfg, addr, vni);
 	if (!f) {
 		NL_SET_ERR_MSG(extack, "Fdb entry not found");
 		err = -ENOENT;
@@ -1423,6 +1436,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 +1455,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 +1491,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);
 	}
@@ -1507,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);
@@ -1516,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);
@@ -1587,6 +1602,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 +1628,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 +1662,21 @@ 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;
+	const struct vxlan_config *cfg;
 	__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)
@@ -1685,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
@@ -1698,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, 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;
@@ -1715,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;
 	}
@@ -1741,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, vs, skb, vni);
+		reason = vxlan_set_mac(vxlan, cfg, vs, skb, vni);
 		if (reason)
 			goto drop;
 	} else {
@@ -1769,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, vni, vninode,
+		vxlan_vnifilter_count(vxlan, cfg, vni, vninode,
 				      VXLAN_VNI_STATS_RX_ERRORS, 0);
 		goto drop;
 	}
@@ -1781,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, vni, vninode,
+		vxlan_vnifilter_count(vxlan, cfg, vni, vninode,
 				      VXLAN_VNI_STATS_RX_ERRORS, 0);
 		goto drop;
 	}
@@ -1791,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, 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, 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();
@@ -1839,20 +1860,23 @@ 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);
+	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, vni, NULL,
+		vxlan_vnifilter_count(vxlan, cfg, vni, NULL,
 				      VXLAN_VNI_STATS_TX_DROPS, 0);
 		goto out;
 	}
@@ -1890,7 +1914,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, cfg, n->ha, vni);
 		if (f)
 			rdst = first_remote_rcu(f);
 		if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -1916,11 +1940,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, 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,9 +2051,10 @@ 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 vxlan_config *cfg;
 	const struct in6_addr *daddr;
 	const struct ipv6hdr *iphdr;
 	struct neighbour *n;
@@ -2039,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);
@@ -2059,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, 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)) {
@@ -2078,10 +2105,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, 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 +2124,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 +2142,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 +2168,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 +2280,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 +2304,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 +2330,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 +2346,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 +2369,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 +2417,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 +2442,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 +2457,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 +2477,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 +2490,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 +2524,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 +2561,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 +2611,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 +2635,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 +2656,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 +2675,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 +2697,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 +2705,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 +2731,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 +2743,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 +2760,45 @@ 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;
+
+	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);
 
 	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 +2808,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 +2833,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 +2860,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 +2872,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);
 	}
@@ -2846,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;
 
@@ -2861,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)) {
@@ -2875,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);
@@ -2905,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;
@@ -2931,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);
@@ -2941,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);
@@ -2956,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);
@@ -2968,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;
@@ -2990,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)
@@ -3208,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;
 	}
@@ -3228,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);
@@ -3342,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)
 {
@@ -3350,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);
 
@@ -3630,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();
@@ -3654,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);
@@ -3669,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);
@@ -3679,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);
@@ -3701,28 +3782,33 @@ 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;
 
 	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;
@@ -3884,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)
@@ -3892,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)
@@ -3901,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);
 
@@ -3925,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;
 	}
 
@@ -3937,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,
@@ -3946,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;
 }
@@ -3964,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;
 
@@ -3972,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);
@@ -4003,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,
@@ -4067,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]));
@@ -4415,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;
 
@@ -4431,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 &&
@@ -4452,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);
@@ -4460,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,
@@ -4476,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;
 			}
 		}
@@ -4490,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))
@@ -4559,11 +4671,22 @@ 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;
+	int err = 0;
+
+	dst = &vxlan->default_dst;
+
+	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);
 
 	if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst->remote_vni)))
 		goto nla_put_failure;
@@ -4585,84 +4708,87 @@ 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;
+out:
+	rcu_read_unlock();
+	return err;
 
 nla_put_failure:
+	rcu_read_unlock();
 	return -EMSGSIZE;
 }
 
diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
index 055a4969f593c9..eb15bb052a6a87 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 = rtnl_dereference(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,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;
+
+	vcfg = rtnl_dereference(vxlan->cfg);
 
 	memset(cfg, 0, sizeof(*cfg));
 	cfg->vxlan = vxlan;
@@ -613,7 +617,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,11 +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 = 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));
@@ -959,7 +964,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 +973,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;
@@ -1202,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;
 
@@ -1219,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);
@@ -1607,6 +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;
 	struct vxlan_mdb_entry *mdb_entry;
 	struct vxlan_mdb_entry_key group;
 
@@ -1614,10 +1620,14 @@ 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.
 	 */
-	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));
@@ -1687,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;
 
@@ -1704,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, src_vni,
+			vxlan_xmit_one(skb1, vxlan->dev, cfg, src_vni,
 				       rcu_dereference(remote->rd), false);
 	}
 
 	if (fremote)
-		vxlan_xmit_one(skb, vxlan->dev, 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);
@@ -1740,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 3b75b48dc726df..e2cf10da274f1b 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_private.h b/drivers/net/vxlan/vxlan_private.h
index b1eec221636088..3d5f21c11ab4ff 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 3e76f4e210944f..f3dda671b4e456 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->flags & VXLAN_F_VNIFILTER))
 		return;
 
 	if (vninode) {
@@ -336,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) */
@@ -477,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;
 
@@ -486,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,
@@ -500,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);
@@ -614,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
@@ -625,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);
@@ -725,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;
@@ -733,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;
@@ -942,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;
@@ -966,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 dfba89695efcfa..7321768b0f2c1e 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 c1b37b50d29e15..9b1101f6ce1e1c 100644
--- a/net/openvswitch/vport-vxlan.c
+++ b/net/openvswitch/vport-vxlan.c
@@ -22,26 +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);
-	__be16 dst_port = vxlan->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 (vxlan->cfg.flags & VXLAN_F_GBP) {
+	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 (vxlan->cfg.flags & VXLAN_F_GBP &&
-		    nla_put_flag(skb, OVS_VXLAN_EXT_GBP))
-			return -EMSGSIZE;
+		if (cfg->flags & VXLAN_F_GBP &&
+		    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] = {

3/2 2026/07/09 09:54 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 36680
							output: 27
							thoughts: 491
						

Thoughts:
**VXLAN RCU Pointer Patch Analysis & Fuzzing Strategy**

Okay, so we've got a patch that's fundamentally changing how the VXLAN configuration (`vxlan->cfg`) is accessed. It's moving from an embedded struct to an RCU-protected pointer, which, as a functional change, has the potential to introduce interesting bugs. This is a core change touching `vxlan_rcv`, `vxlan_xmit`, `vxlan_fdb_create`, `vxlan_changelink`, and a bunch of other key functions across the VXLAN driver (`drivers/net/vxlan/*`, `drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c`, etc.). Definitely fuzz-worthy.

My first thought is to zero in on specific functions to target with fuzzing. Let's see... `vxlan_changelink`, `vxlan_dev_configure`, `vxlan_fill_info`, `vxlan_xmit`, `vxlan_rcv`, `vxlan_fdb_create`, `vxlan_mdb_config_init`, `vxlan_multicast_join`, `vxlan_multicast_leave`, `vxlan_vni_add`, `vxlan_vnifilter_process`, and so on. Wait, `vxlan_rcv` and `vxlan_xmit` are hot-path functions. We probably *don't* want to start there; let's leave the performance critical code alone for now.

We can focus on control-path functions instead. That looks like:

*   `vxlan_changelink`
*   `vxlan_dev_configure`
*   `vxlan_fill_info`
*   `vxlan_fdb_create`
*   `vxlan_mdb_config_init`
*   `vxlan_vni_add`
*   `vxlan_vnifilter_process`
*   `vxlan_multicast_join`
*   `vxlan_multicast_leave`
*   `vxlan_config_apply`

Next, I need to make sure I've got the necessary configs enabled. I'll need to check `drivers/net/vxlan/Kconfig`. Time to `grepper` for those VXLAN-related configs and see what we need.



4/2 2026/07/09 09:55 tool grepper
0m Args:
{
  "Expression": "config VXLAN",
  "PathPrefix": "drivers/net/"
}

Results:
{
  "Output": "drivers/net/Kconfig=216=config IPVTAP\n--\ndrivers/net/Kconfig-229-\ndrivers/net/Kconfig:230:config VXLAN\ndrivers/net/Kconfig-231-\ttristate \"Virtual eXtensible Local Area Network (VXLAN)\"\n"
}

5/2 2026/07/09 09:55 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 37800
							output: 38
							thoughts: 20
						

Thoughts:
Here's my take on the thought process:

**Configuration Exploration**

Okay, so I'm looking at this setup and the key point seems to be `VXLAN`. That's where I'm starting. I need to understand its role and how it's implemented. I'm going to quickly scan for other configurations, though. It's crucial to know if `VXLAN` is the *only* major configuration element, or if it's part of a larger, interconnected system. Understanding the relationship between `VXLAN` and other potential configs is going to be important to get the full picture. Let's see what else is in play.



6/2 2026/07/09 09:55 tool grepper
0m Args:
{
  "Expression": "config MLXSW",
  "PathPrefix": "drivers/net/ethernet/mellanox/mlxsw/"
}

Results:
{
  "Output": "drivers/net/ethernet/mellanox/mlxsw/Kconfig-5-\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig:6:config MLXSW_CORE\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-7-\ttristate \"Mellanox Technologies Switch ASICs support\"\n--\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-16-\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig:17:config MLXSW_CORE_HWMON\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-18-\tbool \"HWMON support for Mellanox Technologies Switch ASICs\"\n--\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-24-\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig:25:config MLXSW_CORE_THERMAL\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-26-\tbool \"Thermal zone support for Mellanox Technologies Switch ASICs\"\n--\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-32-\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig:33:config MLXSW_PCI\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-34-\ttristate \"PCI bus implementation for Mellanox Technologies Switch ASICs\"\n--\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-43-\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig:44:config MLXSW_I2C\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-45-\ttristate \"I2C bus implementation for Mellanox Technologies Switch ASICs\"\n--\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-53-\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig:54:config MLXSW_SPECTRUM\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-55-\ttristate \"Mellanox Technologies Spectrum family support\"\n--\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-74-\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig:75:config MLXSW_SPECTRUM_DCB\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-76-\tbool \"Data Center Bridging (DCB) support\"\n--\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-82-\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig:83:config MLXSW_MINIMAL\ndrivers/net/ethernet/mellanox/mlxsw/Kconfig-84-\ttristate \"Mellanox Technologies minimal I2C support\"\n"
}

7/2 2026/07/09 09:55 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 38711
							output: 31
							thoughts: 79
						

Thoughts:
Okay, let's break this down.

**Analyzing the Code Changes: A Network Expert's Perspective**

Alright, my initial observation is that the patch clearly hits three key areas: `drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c`, `spectrum_switchdev.c`, and surprisingly, `net/openvswitch/vport-vxlan.c`. This strongly suggests a focus on VXLAN handling, likely in the context of Mellanox Spectrum switches. The presence of `mlxsw` in the directory, combined with the target files, confirms that `MLXSW_SPECTRUM` is definitely a key factor here. I need to keep that module in mind as I dig deeper. My next move is clear - let's dive into the configuration within `net/openvswitch/vport-vxlan.c` to see how VXLAN is being handled and how it might interface with the Mellanox hardware. This patch clearly touches a complex interplay between the kernel's VXLAN implementation and a hardware-specific switch driver. This merits careful attention.



8/2 2026/07/09 09:55 tool grepper
0m Args:
{
  "Expression": "config OPENVSWITCH",
  "PathPrefix": "net/openvswitch/"
}

Results:
{
  "Output": "net/openvswitch/Kconfig-5-\nnet/openvswitch/Kconfig:6:config OPENVSWITCH\nnet/openvswitch/Kconfig-7-\ttristate \"Open vSwitch\"\n--\nnet/openvswitch/Kconfig-43-\nnet/openvswitch/Kconfig:44:config OPENVSWITCH_GRE\nnet/openvswitch/Kconfig-45-\ttristate \"Open vSwitch GRE tunneling support\"\n--\nnet/openvswitch/Kconfig-56-\nnet/openvswitch/Kconfig:57:config OPENVSWITCH_VXLAN\nnet/openvswitch/Kconfig-58-\ttristate \"Open vSwitch VXLAN tunneling support\"\n--\nnet/openvswitch/Kconfig-68-\nnet/openvswitch/Kconfig:69:config OPENVSWITCH_GENEVE\nnet/openvswitch/Kconfig-70-\ttristate \"Open vSwitch Geneve tunneling support\"\n"
}

9/2 2026/07/09 09:55 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 39305
							output: 263
							thoughts: 860
						

Thoughts:
**VXLAN Kernel Module Deep Dive**

Okay, so I'm diving into this VXLAN implementation, and it looks like `OPENVSWITCH_VXLAN` is a key area of focus. My initial scan points to a few relevant configurations: `VXLAN`, obviously, `MLXSW_SPECTRUM`, and again, `OPENVSWITCH_VXLAN`. Time to get specific.

I've compiled a list of functions that warrant a closer look. It's a pretty comprehensive list, but I need to prioritize, as some will be more critical than others. The list includes: `vxlan_changelink`, `vxlan_dev_configure`, `vxlan_fill_info`, `vxlan_fdb_create`, `vxlan_mdb_config_init`, `vxlan_vni_add`, `vxlan_vnifilter_process`, `vxlan_multicast_join`, `vxlan_multicast_leave`, `vxlan_config_apply`, `vxlan_nl2conf`, `vxlan_fill_info`, `vxlan_change_mtu`, `vxlan_get_size`, `vxlan_mdb_entry_info_fill`, `vxlan_mdb_nlmsg_remote_size`, `vxlan_mdb_entry_get`, `vxlan_mdb_entry_skb_get`, `vxlan_mdb_xmit`, `vxlan_multicast_join`, `vxlan_multicast_leave`, `vxlan_vnifilter_dump_dev`, `vxlan_update_default_fdb_entry`, `vxlan_vni_delete_group`, `vxlan_vni_add`, `vxlan_vnifilter_process`, `ovs_vxlan_netdev_vport_ops` which contains `vxlan_get_options`, `mlxsw_sp_nve_vxlan_can_offload`, `mlxsw_sp_nve_vxlan_config`, `mlxsw_sp_bridge_vlan_aware_vxlan_join`, `mlxsw_sp_bridge_8021d_vxlan_join`, `__mlxsw_sp_bridge_vxlan_leave`, `mlxsw_sp_fdb_vxlan_call_notifiers`, `__mlxsw_sp_fdb_notify_mac_uc_tunnel_process`, `mlxsw_sp_switchdev_vxlan_work_prepare`, `mlxsw_sp_switchdev_vxlan_vlan_add`, `mlxsw_sp_switchdev_vxlan_vlan_del`.

Now, let's narrow this down to the core functions. I think `vxlan_changelink`, `vxlan_dev_configure`, `vxlan_fdb_create`, `vxlan_nl2conf`, `vxlan_fill_info`, `vxlan_mdb_config_init`, `vxlan_vni_add`, `vxlan_vnifilter_process`, `vxlan_get_options`, and `mlxsw_sp_nve_vxlan_config` are the most critical starting points. I'm going to quickly check my assumptions.

Okay, `vxlan_changelink` makes sense, that handles the netlink interactions for changing link properties. `vxlan_dev_configure` is obviously the device creation and initial config. `vxlan_fdb_create` is for FDB entries, so that's a key piece. `vxlan_nl2conf` is all about parsing netlink attributes, which is essential for configuration. `vxlan_fill_info` deals with dumping information for netlink. `vxlan_mdb_config_init` is for Multicast Database configuration. `vxlan_vni_add` is VNI filtering related. `vxlan_vnifilter_process` is netlink function for VNI filtering. Then, `vxlan_get_options` is specifically for Open vSwitch. I need to remember that!

So I feel I have identified some key areas. Now I can refine these choices and dig in deeper.



10/2 2026/07/09 09:55 tool set-results
0m Args:
{
  "EnableConfigs": [
    "VXLAN",
    "MLXSW_SPECTRUM",
    "OPENVSWITCH_VXLAN"
  ],
  "FocusSymbols": [
    "vxlan_changelink",
    "vxlan_dev_configure",
    "vxlan_fdb_create",
    "vxlan_nl2conf",
    "vxlan_fill_info",
    "vxlan_mdb_config_init",
    "vxlan_vni_add",
    "vxlan_vnifilter_process",
    "vxlan_get_options",
    "mlxsw_sp_nve_vxlan_config"
  ],
  "Reasoning": "The patch changes how the VXLAN configuration (`vxlan-\u003ecfg`) is accessed, moving it from an embedded struct to an RCU-protected pointer. This is a significant functional change in the core VXLAN driver, affecting device creation, configuration, FDB, MDB, VNI filtering, and integration with other subsystems like Open vSwitch and Mellanox Spectrum switchdev. It introduces new memory allocations and RCU dereferences, which should be fuzzed to ensure no use-after-free or memory leaks occur.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "VXLAN",
    "MLXSW_SPECTRUM",
    "OPENVSWITCH_VXLAN"
  ],
  "FocusSymbols": [
    "vxlan_changelink",
    "vxlan_dev_configure",
    "vxlan_fdb_create",
    "vxlan_nl2conf",
    "vxlan_fill_info",
    "vxlan_mdb_config_init",
    "vxlan_vni_add",
    "vxlan_vnifilter_process",
    "vxlan_get_options",
    "mlxsw_sp_nve_vxlan_config"
  ],
  "Reasoning": "The patch changes how the VXLAN configuration (`vxlan-\u003ecfg`) is accessed, moving it from an embedded struct to an RCU-protected pointer. This is a significant functional change in the core VXLAN driver, affecting device creation, configuration, FDB, MDB, VNI filtering, and integration with other subsystems like Open vSwitch and Mellanox Spectrum switchdev. It introduces new memory allocations and RCU dereferences, which should be fuzzed to ensure no use-after-free or memory leaks occur.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)