udp_tunnel: remove rtnl_lock dependency

Drivers that are using ops lock and don't depend on RTNL lock
still need to manage it because udp_tunnel's RTNL dependency.
Introduce new udp_tunnel_nic_lock and use it instead of
rtnl_lock. Drop non-UDP_TUNNEL_NIC_INFO_MAY_SLEEP mode from
udp_tunnel infra (udp_tunnel_nic_device_sync_work needs to
grab udp_tunnel_nic_lock mutex and might sleep).

Cover more places in v4:

- netlink
  - udp_tunnel_notify_add_rx_port (ndo_open)
    - triggers udp_tunnel_nic_device_sync_work
  - udp_tunnel_notify_del_rx_port (ndo_stop)
    - triggers udp_tunnel_nic_device_sync_work
  - udp_tunnel_get_rx_info (__netdev_update_features)
    - triggers NETDEV_UDP_TUNNEL_PUSH_INFO
  - udp_tunnel_drop_rx_info (__netdev_update_features)
    - triggers NETDEV_UDP_TUNNEL_DROP_INFO
  - udp_tunnel_nic_reset_ntf (ndo_open)

- notifiers
  - udp_tunnel_nic_netdevice_event, depending on the event:
    - triggers NETDEV_UDP_TUNNEL_PUSH_INFO
    - triggers NETDEV_UDP_TUNNEL_DROP_INFO

- ethnl_tunnel_info_reply_size
- udp_tunnel_nic_set_port_priv (two intel drivers)

Cc: Michael Chan <michael.chan@broadcom.com>
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Stanislav Fomichev <stfomichev@gmail.com>
Link: https://patch.msgid.link/20250616162117.287806-4-stfomichev@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Stanislav Fomichev 2025-06-16 09:21:14 -07:00 committed by Jakub Kicinski
parent df5425b3bd
commit 1ead750109
16 changed files with 142 additions and 73 deletions

View file

@ -10219,8 +10219,7 @@ static int bnx2x_udp_tunnel_sync(struct net_device *netdev, unsigned int table)
static const struct udp_tunnel_nic_info bnx2x_udp_tunnels = { static const struct udp_tunnel_nic_info bnx2x_udp_tunnels = {
.sync_table = bnx2x_udp_tunnel_sync, .sync_table = bnx2x_udp_tunnel_sync,
.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP | .flags = UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
.tables = { .tables = {
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_GENEVE, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_GENEVE, },

View file

@ -15573,8 +15573,7 @@ static int bnxt_udp_tunnel_unset_port(struct net_device *netdev, unsigned int ta
static const struct udp_tunnel_nic_info bnxt_udp_tunnels = { static const struct udp_tunnel_nic_info bnxt_udp_tunnels = {
.set_port = bnxt_udp_tunnel_set_port, .set_port = bnxt_udp_tunnel_set_port,
.unset_port = bnxt_udp_tunnel_unset_port, .unset_port = bnxt_udp_tunnel_unset_port,
.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP | .flags = UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
.tables = { .tables = {
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_GENEVE, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_GENEVE, },
@ -15582,8 +15581,7 @@ static const struct udp_tunnel_nic_info bnxt_udp_tunnels = {
}, bnxt_udp_tunnels_p7 = { }, bnxt_udp_tunnels_p7 = {
.set_port = bnxt_udp_tunnel_set_port, .set_port = bnxt_udp_tunnel_set_port,
.unset_port = bnxt_udp_tunnel_unset_port, .unset_port = bnxt_udp_tunnel_unset_port,
.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP | .flags = UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
.tables = { .tables = {
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_GENEVE, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_GENEVE, },

View file

@ -4031,8 +4031,7 @@ static int be_vxlan_unset_port(struct net_device *netdev, unsigned int table,
static const struct udp_tunnel_nic_info be_udp_tunnels = { static const struct udp_tunnel_nic_info be_udp_tunnels = {
.set_port = be_vxlan_set_port, .set_port = be_vxlan_set_port,
.unset_port = be_vxlan_unset_port, .unset_port = be_vxlan_unset_port,
.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP | .flags = UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
.tables = { .tables = {
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
}, },

View file

@ -15895,7 +15895,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pf->udp_tunnel_nic.set_port = i40e_udp_tunnel_set_port; pf->udp_tunnel_nic.set_port = i40e_udp_tunnel_set_port;
pf->udp_tunnel_nic.unset_port = i40e_udp_tunnel_unset_port; pf->udp_tunnel_nic.unset_port = i40e_udp_tunnel_unset_port;
pf->udp_tunnel_nic.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP;
pf->udp_tunnel_nic.shared = &pf->udp_tunnel_shared; pf->udp_tunnel_nic.shared = &pf->udp_tunnel_shared;
pf->udp_tunnel_nic.tables[0].n_entries = I40E_MAX_PF_UDP_OFFLOAD_PORTS; pf->udp_tunnel_nic.tables[0].n_entries = I40E_MAX_PF_UDP_OFFLOAD_PORTS;
pf->udp_tunnel_nic.tables[0].tunnel_types = UDP_TUNNEL_TYPE_VXLAN | pf->udp_tunnel_nic.tables[0].tunnel_types = UDP_TUNNEL_TYPE_VXLAN |

View file

@ -4767,7 +4767,6 @@ int ice_init_dev(struct ice_pf *pf)
pf->hw.udp_tunnel_nic.set_port = ice_udp_tunnel_set_port; pf->hw.udp_tunnel_nic.set_port = ice_udp_tunnel_set_port;
pf->hw.udp_tunnel_nic.unset_port = ice_udp_tunnel_unset_port; pf->hw.udp_tunnel_nic.unset_port = ice_udp_tunnel_unset_port;
pf->hw.udp_tunnel_nic.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP;
pf->hw.udp_tunnel_nic.shared = &pf->hw.udp_tunnel_shared; pf->hw.udp_tunnel_nic.shared = &pf->hw.udp_tunnel_shared;
if (pf->hw.tnl.valid_count[TNL_VXLAN]) { if (pf->hw.tnl.valid_count[TNL_VXLAN]) {
pf->hw.udp_tunnel_nic.tables[0].n_entries = pf->hw.udp_tunnel_nic.tables[0].n_entries =

View file

@ -2670,8 +2670,7 @@ static int mlx4_udp_tunnel_sync(struct net_device *dev, unsigned int table)
static const struct udp_tunnel_nic_info mlx4_udp_tunnels = { static const struct udp_tunnel_nic_info mlx4_udp_tunnels = {
.sync_table = mlx4_udp_tunnel_sync, .sync_table = mlx4_udp_tunnel_sync,
.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP | .flags = UDP_TUNNEL_NIC_INFO_IPV4_ONLY,
UDP_TUNNEL_NIC_INFO_IPV4_ONLY,
.tables = { .tables = {
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
}, },

View file

@ -5351,8 +5351,7 @@ void mlx5e_vxlan_set_netdev_info(struct mlx5e_priv *priv)
priv->nic_info.set_port = mlx5e_vxlan_set_port; priv->nic_info.set_port = mlx5e_vxlan_set_port;
priv->nic_info.unset_port = mlx5e_vxlan_unset_port; priv->nic_info.unset_port = mlx5e_vxlan_unset_port;
priv->nic_info.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP | priv->nic_info.flags = UDP_TUNNEL_NIC_INFO_STATIC_IANA_VXLAN;
UDP_TUNNEL_NIC_INFO_STATIC_IANA_VXLAN;
priv->nic_info.tables[0].tunnel_types = UDP_TUNNEL_TYPE_VXLAN; priv->nic_info.tables[0].tunnel_types = UDP_TUNNEL_TYPE_VXLAN;
/* Don't count the space hard-coded to the IANA port */ /* Don't count the space hard-coded to the IANA port */
priv->nic_info.tables[0].n_entries = priv->nic_info.tables[0].n_entries =

View file

@ -2394,8 +2394,7 @@ static int nfp_udp_tunnel_sync(struct net_device *netdev, unsigned int table)
static const struct udp_tunnel_nic_info nfp_udp_tunnels = { static const struct udp_tunnel_nic_info nfp_udp_tunnels = {
.sync_table = nfp_udp_tunnel_sync, .sync_table = nfp_udp_tunnel_sync,
.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP | .flags = UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
UDP_TUNNEL_NIC_INFO_OPEN_ONLY,
.tables = { .tables = {
{ {
.n_entries = NFP_NET_N_VXLAN_PORTS, .n_entries = NFP_NET_N_VXLAN_PORTS,

View file

@ -987,20 +987,17 @@ static int qede_udp_tunnel_sync(struct net_device *dev, unsigned int table)
static const struct udp_tunnel_nic_info qede_udp_tunnels_both = { static const struct udp_tunnel_nic_info qede_udp_tunnels_both = {
.sync_table = qede_udp_tunnel_sync, .sync_table = qede_udp_tunnel_sync,
.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP,
.tables = { .tables = {
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_GENEVE, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_GENEVE, },
}, },
}, qede_udp_tunnels_vxlan = { }, qede_udp_tunnels_vxlan = {
.sync_table = qede_udp_tunnel_sync, .sync_table = qede_udp_tunnel_sync,
.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP,
.tables = { .tables = {
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
}, },
}, qede_udp_tunnels_geneve = { }, qede_udp_tunnels_geneve = {
.sync_table = qede_udp_tunnel_sync, .sync_table = qede_udp_tunnel_sync,
.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP,
.tables = { .tables = {
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_GENEVE, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_GENEVE, },
}, },

View file

@ -486,7 +486,6 @@ static int qlcnic_udp_tunnel_sync(struct net_device *dev, unsigned int table)
static const struct udp_tunnel_nic_info qlcnic_udp_tunnels = { static const struct udp_tunnel_nic_info qlcnic_udp_tunnels = {
.sync_table = qlcnic_udp_tunnel_sync, .sync_table = qlcnic_udp_tunnel_sync,
.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP,
.tables = { .tables = {
{ .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
}, },

View file

@ -3985,7 +3985,6 @@ static int efx_ef10_udp_tnl_unset_port(struct net_device *dev,
static const struct udp_tunnel_nic_info efx_ef10_udp_tunnels = { static const struct udp_tunnel_nic_info efx_ef10_udp_tunnels = {
.set_port = efx_ef10_udp_tnl_set_port, .set_port = efx_ef10_udp_tnl_set_port,
.unset_port = efx_ef10_udp_tnl_unset_port, .unset_port = efx_ef10_udp_tnl_unset_port,
.flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP,
.tables = { .tables = {
{ {
.n_entries = 16, .n_entries = 16,

View file

@ -112,12 +112,10 @@ nsim_udp_tunnels_info_reset_write(struct file *file, const char __user *data,
struct net_device *dev = file->private_data; struct net_device *dev = file->private_data;
struct netdevsim *ns = netdev_priv(dev); struct netdevsim *ns = netdev_priv(dev);
rtnl_lock();
if (dev->reg_state == NETREG_REGISTERED) { if (dev->reg_state == NETREG_REGISTERED) {
memset(ns->udp_ports.ports, 0, sizeof(ns->udp_ports.__ports)); memset(ns->udp_ports.ports, 0, sizeof(ns->udp_ports.__ports));
udp_tunnel_nic_reset_ntf(dev); udp_tunnel_nic_reset_ntf(dev);
} }
rtnl_unlock();
return count; return count;
} }
@ -181,8 +179,6 @@ int nsim_udp_tunnels_info_create(struct nsim_dev *nsim_dev,
info->sync_table = NULL; info->sync_table = NULL;
} }
if (ns->udp_ports.sleep)
info->flags |= UDP_TUNNEL_NIC_INFO_MAY_SLEEP;
if (nsim_dev->udp_ports.open_only) if (nsim_dev->udp_ports.open_only)
info->flags |= UDP_TUNNEL_NIC_INFO_OPEN_ONLY; info->flags |= UDP_TUNNEL_NIC_INFO_OPEN_ONLY;
if (nsim_dev->udp_ports.ipv4_only) if (nsim_dev->udp_ports.ipv4_only)

View file

@ -130,22 +130,6 @@ void udp_tunnel_drop_rx_port(struct net_device *dev, struct socket *sock,
void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type); void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type);
void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type); void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type);
static inline void udp_tunnel_get_rx_info(struct net_device *dev)
{
ASSERT_RTNL();
if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
return;
call_netdevice_notifiers(NETDEV_UDP_TUNNEL_PUSH_INFO, dev);
}
static inline void udp_tunnel_drop_rx_info(struct net_device *dev)
{
ASSERT_RTNL();
if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
return;
call_netdevice_notifiers(NETDEV_UDP_TUNNEL_DROP_INFO, dev);
}
/* Transmit the skb using UDP encapsulation. */ /* Transmit the skb using UDP encapsulation. */
void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
__be32 src, __be32 dst, __u8 tos, __u8 ttl, __be32 src, __be32 dst, __u8 tos, __u8 ttl,
@ -222,19 +206,17 @@ static inline void udp_tunnel_encap_enable(struct sock *sk)
#define UDP_TUNNEL_NIC_MAX_TABLES 4 #define UDP_TUNNEL_NIC_MAX_TABLES 4
enum udp_tunnel_nic_info_flags { enum udp_tunnel_nic_info_flags {
/* Device callbacks may sleep */
UDP_TUNNEL_NIC_INFO_MAY_SLEEP = BIT(0),
/* Device only supports offloads when it's open, all ports /* Device only supports offloads when it's open, all ports
* will be removed before close and re-added after open. * will be removed before close and re-added after open.
*/ */
UDP_TUNNEL_NIC_INFO_OPEN_ONLY = BIT(1), UDP_TUNNEL_NIC_INFO_OPEN_ONLY = BIT(0),
/* Device supports only IPv4 tunnels */ /* Device supports only IPv4 tunnels */
UDP_TUNNEL_NIC_INFO_IPV4_ONLY = BIT(2), UDP_TUNNEL_NIC_INFO_IPV4_ONLY = BIT(1),
/* Device has hard-coded the IANA VXLAN port (4789) as VXLAN. /* Device has hard-coded the IANA VXLAN port (4789) as VXLAN.
* This port must not be counted towards n_entries of any table. * This port must not be counted towards n_entries of any table.
* Driver will not receive any callback associated with port 4789. * Driver will not receive any callback associated with port 4789.
*/ */
UDP_TUNNEL_NIC_INFO_STATIC_IANA_VXLAN = BIT(3), UDP_TUNNEL_NIC_INFO_STATIC_IANA_VXLAN = BIT(2),
}; };
struct udp_tunnel_nic; struct udp_tunnel_nic;
@ -325,6 +307,9 @@ struct udp_tunnel_nic_ops {
size_t (*dump_size)(struct net_device *dev, unsigned int table); size_t (*dump_size)(struct net_device *dev, unsigned int table);
int (*dump_write)(struct net_device *dev, unsigned int table, int (*dump_write)(struct net_device *dev, unsigned int table,
struct sk_buff *skb); struct sk_buff *skb);
void (*assert_locked)(struct net_device *dev);
void (*lock)(struct net_device *dev);
void (*unlock)(struct net_device *dev);
}; };
#ifdef CONFIG_INET #ifdef CONFIG_INET
@ -353,8 +338,29 @@ static inline void
udp_tunnel_nic_set_port_priv(struct net_device *dev, unsigned int table, udp_tunnel_nic_set_port_priv(struct net_device *dev, unsigned int table,
unsigned int idx, u8 priv) unsigned int idx, u8 priv)
{ {
if (udp_tunnel_nic_ops) if (udp_tunnel_nic_ops) {
udp_tunnel_nic_ops->lock(dev);
udp_tunnel_nic_ops->set_port_priv(dev, table, idx, priv); udp_tunnel_nic_ops->set_port_priv(dev, table, idx, priv);
udp_tunnel_nic_ops->unlock(dev);
}
}
static inline void udp_tunnel_nic_assert_locked(struct net_device *dev)
{
if (udp_tunnel_nic_ops)
udp_tunnel_nic_ops->assert_locked(dev);
}
static inline void udp_tunnel_nic_lock(struct net_device *dev)
{
if (udp_tunnel_nic_ops)
udp_tunnel_nic_ops->lock(dev);
}
static inline void udp_tunnel_nic_unlock(struct net_device *dev)
{
if (udp_tunnel_nic_ops)
udp_tunnel_nic_ops->unlock(dev);
} }
static inline void static inline void
@ -396,17 +402,50 @@ static inline void udp_tunnel_nic_reset_ntf(struct net_device *dev)
static inline size_t static inline size_t
udp_tunnel_nic_dump_size(struct net_device *dev, unsigned int table) udp_tunnel_nic_dump_size(struct net_device *dev, unsigned int table)
{ {
size_t ret;
if (!udp_tunnel_nic_ops) if (!udp_tunnel_nic_ops)
return 0; return 0;
return udp_tunnel_nic_ops->dump_size(dev, table);
udp_tunnel_nic_ops->lock(dev);
ret = udp_tunnel_nic_ops->dump_size(dev, table);
udp_tunnel_nic_ops->unlock(dev);
return ret;
} }
static inline int static inline int
udp_tunnel_nic_dump_write(struct net_device *dev, unsigned int table, udp_tunnel_nic_dump_write(struct net_device *dev, unsigned int table,
struct sk_buff *skb) struct sk_buff *skb)
{ {
int ret;
if (!udp_tunnel_nic_ops) if (!udp_tunnel_nic_ops)
return 0; return 0;
return udp_tunnel_nic_ops->dump_write(dev, table, skb);
udp_tunnel_nic_ops->lock(dev);
ret = udp_tunnel_nic_ops->dump_write(dev, table, skb);
udp_tunnel_nic_ops->unlock(dev);
return ret;
} }
static inline void udp_tunnel_get_rx_info(struct net_device *dev)
{
ASSERT_RTNL();
if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
return;
udp_tunnel_nic_assert_locked(dev);
call_netdevice_notifiers(NETDEV_UDP_TUNNEL_PUSH_INFO, dev);
}
static inline void udp_tunnel_drop_rx_info(struct net_device *dev)
{
ASSERT_RTNL();
if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
return;
udp_tunnel_nic_assert_locked(dev);
call_netdevice_notifiers(NETDEV_UDP_TUNNEL_DROP_INFO, dev);
}
#endif #endif

View file

@ -10771,12 +10771,14 @@ sync_lower:
* *before* calling udp_tunnel_get_rx_info, * *before* calling udp_tunnel_get_rx_info,
* but *after* calling udp_tunnel_drop_rx_info. * but *after* calling udp_tunnel_drop_rx_info.
*/ */
udp_tunnel_nic_lock(dev);
if (features & NETIF_F_RX_UDP_TUNNEL_PORT) { if (features & NETIF_F_RX_UDP_TUNNEL_PORT) {
dev->features = features; dev->features = features;
udp_tunnel_get_rx_info(dev); udp_tunnel_get_rx_info(dev);
} else { } else {
udp_tunnel_drop_rx_info(dev); udp_tunnel_drop_rx_info(dev);
} }
udp_tunnel_nic_unlock(dev);
} }
if (diff & NETIF_F_HW_VLAN_CTAG_FILTER) { if (diff & NETIF_F_HW_VLAN_CTAG_FILTER) {

View file

@ -134,15 +134,17 @@ void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type)
struct udp_tunnel_info ti; struct udp_tunnel_info ti;
struct net_device *dev; struct net_device *dev;
ASSERT_RTNL();
ti.type = type; ti.type = type;
ti.sa_family = sk->sk_family; ti.sa_family = sk->sk_family;
ti.port = inet_sk(sk)->inet_sport; ti.port = inet_sk(sk)->inet_sport;
rcu_read_lock(); for_each_netdev(net, dev) {
for_each_netdev_rcu(net, dev) { udp_tunnel_nic_lock(dev);
udp_tunnel_nic_add_port(dev, &ti); udp_tunnel_nic_add_port(dev, &ti);
udp_tunnel_nic_unlock(dev);
} }
rcu_read_unlock();
} }
EXPORT_SYMBOL_GPL(udp_tunnel_notify_add_rx_port); EXPORT_SYMBOL_GPL(udp_tunnel_notify_add_rx_port);
@ -154,15 +156,17 @@ void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type)
struct udp_tunnel_info ti; struct udp_tunnel_info ti;
struct net_device *dev; struct net_device *dev;
ASSERT_RTNL();
ti.type = type; ti.type = type;
ti.sa_family = sk->sk_family; ti.sa_family = sk->sk_family;
ti.port = inet_sk(sk)->inet_sport; ti.port = inet_sk(sk)->inet_sport;
rcu_read_lock(); for_each_netdev(net, dev) {
for_each_netdev_rcu(net, dev) { udp_tunnel_nic_lock(dev);
udp_tunnel_nic_del_port(dev, &ti); udp_tunnel_nic_del_port(dev, &ti);
udp_tunnel_nic_unlock(dev);
} }
rcu_read_unlock();
} }
EXPORT_SYMBOL_GPL(udp_tunnel_notify_del_rx_port); EXPORT_SYMBOL_GPL(udp_tunnel_notify_del_rx_port);

View file

@ -29,6 +29,7 @@ struct udp_tunnel_nic_table_entry {
* struct udp_tunnel_nic - UDP tunnel port offload state * struct udp_tunnel_nic - UDP tunnel port offload state
* @work: async work for talking to hardware from process context * @work: async work for talking to hardware from process context
* @dev: netdev pointer * @dev: netdev pointer
* @lock: protects all fields
* @need_sync: at least one port start changed * @need_sync: at least one port start changed
* @need_replay: space was freed, we need a replay of all ports * @need_replay: space was freed, we need a replay of all ports
* @work_pending: @work is currently scheduled * @work_pending: @work is currently scheduled
@ -41,6 +42,8 @@ struct udp_tunnel_nic {
struct net_device *dev; struct net_device *dev;
struct mutex lock;
u8 need_sync:1; u8 need_sync:1;
u8 need_replay:1; u8 need_replay:1;
u8 work_pending:1; u8 work_pending:1;
@ -298,22 +301,11 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
static void static void
udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
{ {
const struct udp_tunnel_nic_info *info = dev->udp_tunnel_nic_info;
bool may_sleep;
if (!utn->need_sync) if (!utn->need_sync)
return; return;
/* Drivers which sleep in the callback need to update from queue_work(udp_tunnel_nic_workqueue, &utn->work);
* the workqueue, if we come from the tunnel driver's notification. utn->work_pending = 1;
*/
may_sleep = info->flags & UDP_TUNNEL_NIC_INFO_MAY_SLEEP;
if (!may_sleep)
__udp_tunnel_nic_device_sync(dev, utn);
if (may_sleep || utn->need_replay) {
queue_work(udp_tunnel_nic_workqueue, &utn->work);
utn->work_pending = 1;
}
} }
static bool static bool
@ -554,12 +546,12 @@ static void __udp_tunnel_nic_reset_ntf(struct net_device *dev)
struct udp_tunnel_nic *utn; struct udp_tunnel_nic *utn;
unsigned int i, j; unsigned int i, j;
ASSERT_RTNL();
utn = dev->udp_tunnel_nic; utn = dev->udp_tunnel_nic;
if (!utn) if (!utn)
return; return;
mutex_lock(&utn->lock);
utn->need_sync = false; utn->need_sync = false;
for (i = 0; i < utn->n_tables; i++) for (i = 0; i < utn->n_tables; i++)
for (j = 0; j < info->tables[i].n_entries; j++) { for (j = 0; j < info->tables[i].n_entries; j++) {
@ -569,7 +561,7 @@ static void __udp_tunnel_nic_reset_ntf(struct net_device *dev)
entry->flags &= ~(UDP_TUNNEL_NIC_ENTRY_DEL | entry->flags &= ~(UDP_TUNNEL_NIC_ENTRY_DEL |
UDP_TUNNEL_NIC_ENTRY_OP_FAIL); UDP_TUNNEL_NIC_ENTRY_OP_FAIL);
/* We don't release rtnl across ops */ /* We don't release utn lock across ops */
WARN_ON(entry->flags & UDP_TUNNEL_NIC_ENTRY_FROZEN); WARN_ON(entry->flags & UDP_TUNNEL_NIC_ENTRY_FROZEN);
if (!entry->use_cnt) if (!entry->use_cnt)
continue; continue;
@ -579,6 +571,8 @@ static void __udp_tunnel_nic_reset_ntf(struct net_device *dev)
} }
__udp_tunnel_nic_device_sync(dev, utn); __udp_tunnel_nic_device_sync(dev, utn);
mutex_unlock(&utn->lock);
} }
static size_t static size_t
@ -643,6 +637,33 @@ err_cancel:
return -EMSGSIZE; return -EMSGSIZE;
} }
static void __udp_tunnel_nic_assert_locked(struct net_device *dev)
{
struct udp_tunnel_nic *utn;
utn = dev->udp_tunnel_nic;
if (utn)
lockdep_assert_held(&utn->lock);
}
static void __udp_tunnel_nic_lock(struct net_device *dev)
{
struct udp_tunnel_nic *utn;
utn = dev->udp_tunnel_nic;
if (utn)
mutex_lock(&utn->lock);
}
static void __udp_tunnel_nic_unlock(struct net_device *dev)
{
struct udp_tunnel_nic *utn;
utn = dev->udp_tunnel_nic;
if (utn)
mutex_unlock(&utn->lock);
}
static const struct udp_tunnel_nic_ops __udp_tunnel_nic_ops = { static const struct udp_tunnel_nic_ops __udp_tunnel_nic_ops = {
.get_port = __udp_tunnel_nic_get_port, .get_port = __udp_tunnel_nic_get_port,
.set_port_priv = __udp_tunnel_nic_set_port_priv, .set_port_priv = __udp_tunnel_nic_set_port_priv,
@ -651,6 +672,9 @@ static const struct udp_tunnel_nic_ops __udp_tunnel_nic_ops = {
.reset_ntf = __udp_tunnel_nic_reset_ntf, .reset_ntf = __udp_tunnel_nic_reset_ntf,
.dump_size = __udp_tunnel_nic_dump_size, .dump_size = __udp_tunnel_nic_dump_size,
.dump_write = __udp_tunnel_nic_dump_write, .dump_write = __udp_tunnel_nic_dump_write,
.assert_locked = __udp_tunnel_nic_assert_locked,
.lock = __udp_tunnel_nic_lock,
.unlock = __udp_tunnel_nic_unlock,
}; };
static void static void
@ -710,11 +734,15 @@ static void udp_tunnel_nic_device_sync_work(struct work_struct *work)
container_of(work, struct udp_tunnel_nic, work); container_of(work, struct udp_tunnel_nic, work);
rtnl_lock(); rtnl_lock();
mutex_lock(&utn->lock);
utn->work_pending = 0; utn->work_pending = 0;
__udp_tunnel_nic_device_sync(utn->dev, utn); __udp_tunnel_nic_device_sync(utn->dev, utn);
if (utn->need_replay) if (utn->need_replay)
udp_tunnel_nic_replay(utn->dev, utn); udp_tunnel_nic_replay(utn->dev, utn);
mutex_unlock(&utn->lock);
rtnl_unlock(); rtnl_unlock();
} }
@ -730,6 +758,7 @@ udp_tunnel_nic_alloc(const struct udp_tunnel_nic_info *info,
return NULL; return NULL;
utn->n_tables = n_tables; utn->n_tables = n_tables;
INIT_WORK(&utn->work, udp_tunnel_nic_device_sync_work); INIT_WORK(&utn->work, udp_tunnel_nic_device_sync_work);
mutex_init(&utn->lock);
for (i = 0; i < n_tables; i++) { for (i = 0; i < n_tables; i++) {
utn->entries[i] = kcalloc(info->tables[i].n_entries, utn->entries[i] = kcalloc(info->tables[i].n_entries,
@ -821,8 +850,11 @@ static int udp_tunnel_nic_register(struct net_device *dev)
dev_hold(dev); dev_hold(dev);
dev->udp_tunnel_nic = utn; dev->udp_tunnel_nic = utn;
if (!(info->flags & UDP_TUNNEL_NIC_INFO_OPEN_ONLY)) if (!(info->flags & UDP_TUNNEL_NIC_INFO_OPEN_ONLY)) {
udp_tunnel_nic_lock(dev);
udp_tunnel_get_rx_info(dev); udp_tunnel_get_rx_info(dev);
udp_tunnel_nic_unlock(dev);
}
return 0; return 0;
} }
@ -832,6 +864,8 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn)
{ {
const struct udp_tunnel_nic_info *info = dev->udp_tunnel_nic_info; const struct udp_tunnel_nic_info *info = dev->udp_tunnel_nic_info;
udp_tunnel_nic_lock(dev);
/* For a shared table remove this dev from the list of sharing devices /* For a shared table remove this dev from the list of sharing devices
* and if there are other devices just detach. * and if there are other devices just detach.
*/ */
@ -841,8 +875,10 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn)
list_for_each_entry(node, &info->shared->devices, list) list_for_each_entry(node, &info->shared->devices, list)
if (node->dev == dev) if (node->dev == dev)
break; break;
if (list_entry_is_head(node, &info->shared->devices, list)) if (list_entry_is_head(node, &info->shared->devices, list)) {
udp_tunnel_nic_unlock(dev);
return; return;
}
list_del(&node->list); list_del(&node->list);
kfree(node); kfree(node);
@ -852,6 +888,7 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn)
if (first) { if (first) {
udp_tunnel_drop_rx_info(dev); udp_tunnel_drop_rx_info(dev);
utn->dev = first->dev; utn->dev = first->dev;
udp_tunnel_nic_unlock(dev);
goto release_dev; goto release_dev;
} }
@ -862,6 +899,7 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn)
* from the work which we will boot immediately. * from the work which we will boot immediately.
*/ */
udp_tunnel_nic_flush(dev, utn); udp_tunnel_nic_flush(dev, utn);
udp_tunnel_nic_unlock(dev);
/* Wait for the work to be done using the state, netdev core will /* Wait for the work to be done using the state, netdev core will
* retry unregister until we give up our reference on this device. * retry unregister until we give up our reference on this device.
@ -910,12 +948,16 @@ udp_tunnel_nic_netdevice_event(struct notifier_block *unused,
return NOTIFY_DONE; return NOTIFY_DONE;
if (event == NETDEV_UP) { if (event == NETDEV_UP) {
udp_tunnel_nic_lock(dev);
WARN_ON(!udp_tunnel_nic_is_empty(dev, utn)); WARN_ON(!udp_tunnel_nic_is_empty(dev, utn));
udp_tunnel_get_rx_info(dev); udp_tunnel_get_rx_info(dev);
udp_tunnel_nic_unlock(dev);
return NOTIFY_OK; return NOTIFY_OK;
} }
if (event == NETDEV_GOING_DOWN) { if (event == NETDEV_GOING_DOWN) {
udp_tunnel_nic_lock(dev);
udp_tunnel_nic_flush(dev, utn); udp_tunnel_nic_flush(dev, utn);
udp_tunnel_nic_unlock(dev);
return NOTIFY_OK; return NOTIFY_OK;
} }