tipc: Store struct sock in struct udp_bearer.

tipc udp_bearer does not need to access struct socket itself in
the fast path; it only reads struct sock, and struct socket is
only used for tunnel setup and teardown.

Let's store struct sock directly in struct udp_bearer.

Note that cleanup_bearer() calls synchronize_net() after
udp_tunnel_sock_release(), so udp_bearer is not freed until
inflight fast paths finish.

Note also that synchronize_rcu() is added in the error path
of tipc_udp_enable() since udp_bearer will be kfree()d
immediately once we remove synchronize_rcu() in
udp_tunnel_sock_release().

This can be later converted to kfree_rcu().

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20260502031401.3557229-15-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Kuniyuki Iwashima 2026-05-02 03:13:07 +00:00 committed by Jakub Kicinski
parent fe57adbe0d
commit 1ae552c7b6

View File

@ -89,14 +89,14 @@ struct udp_replicast {
/**
* struct udp_bearer - ip/udp bearer data structure
* @bearer: associated generic tipc bearer
* @ubsock: bearer associated socket
* @sk: bearer associated socket
* @ifindex: local address scope
* @work: used to schedule deferred work on a bearer
* @rcast: associated udp_replicast container
*/
struct udp_bearer {
struct tipc_bearer __rcu *bearer;
struct socket *ubsock;
struct sock *sk;
u32 ifindex;
struct work_struct work;
struct udp_replicast rcast;
@ -194,7 +194,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb,
}
ttl = ip4_dst_hoplimit(&rt->dst);
udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, src->ipv4.s_addr,
udp_tunnel_xmit_skb(rt, ub->sk, skb, src->ipv4.s_addr,
dst->ipv4.s_addr, 0, ttl, 0, src->port,
dst->port, false, true, 0);
#if IS_ENABLED(CONFIG_IPV6)
@ -206,7 +206,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb,
.saddr = src->ipv6,
.flowi6_proto = IPPROTO_UDP
};
ndst = ip6_dst_lookup_flow(net, ub->ubsock->sk,
ndst = ip6_dst_lookup_flow(net, ub->sk,
&fl6, NULL);
if (IS_ERR(ndst)) {
err = PTR_ERR(ndst);
@ -215,7 +215,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb,
dst_cache_set_ip6(cache, ndst, &fl6.saddr);
}
ttl = ip6_dst_hoplimit(ndst);
udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, skb, NULL,
udp_tunnel6_xmit_skb(ndst, ub->sk, skb, NULL,
&src->ipv6, &dst->ipv6, 0, ttl, 0,
src->port, dst->port, false, 0);
#endif
@ -405,9 +405,9 @@ static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb)
static int enable_mcast(struct udp_bearer *ub, struct udp_media_addr *remote)
{
int err = 0;
struct sock *sk = ub->sk;
struct ip_mreqn mreqn;
struct sock *sk = ub->ubsock->sk;
int err = 0;
if (ntohs(remote->proto) == ETH_P_IP) {
mreqn.imr_multiaddr = remote->ipv4;
@ -670,6 +670,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
struct nlattr *opts[TIPC_NLA_UDP_MAX + 1];
u8 node_id[NODE_ID_LEN] = {0,};
struct net_device *dev;
struct socket *sock;
int rmcast = 0;
ub = kzalloc_obj(*ub, GFP_ATOMIC);
@ -764,14 +765,16 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
goto err;
}
udp_conf.local_udp_port = local.port;
err = udp_sock_create(net, &udp_conf, &ub->ubsock);
err = udp_sock_create(net, &udp_conf, &sock);
if (err)
goto err;
ub->sk = sock->sk;
tuncfg.sk_user_data = ub;
tuncfg.encap_type = 1;
tuncfg.encap_rcv = tipc_udp_recv;
tuncfg.encap_destroy = NULL;
setup_udp_tunnel_sock(net, ub->ubsock->sk, &tuncfg);
setup_udp_tunnel_sock(net, ub->sk, &tuncfg);
err = dst_cache_init(&ub->rcast.dst_cache, GFP_ATOMIC);
if (err)
@ -793,7 +796,8 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
free:
dst_cache_destroy(&ub->rcast.dst_cache);
udp_tunnel_sock_release(ub->ubsock->sk);
udp_tunnel_sock_release(ub->sk);
synchronize_rcu();
err:
kfree(ub);
return err;
@ -812,10 +816,10 @@ static void cleanup_bearer(struct work_struct *work)
kfree_rcu(rcast, rcu);
}
tn = tipc_net(sock_net(ub->ubsock->sk));
tn = tipc_net(sock_net(ub->sk));
dst_cache_destroy(&ub->rcast.dst_cache);
udp_tunnel_sock_release(ub->ubsock->sk);
udp_tunnel_sock_release(ub->sk);
/* Note: could use a call_rcu() to avoid another synchronize_net() */
synchronize_net();
@ -833,11 +837,11 @@ static void tipc_udp_disable(struct tipc_bearer *b)
pr_err("UDP bearer instance not found\n");
return;
}
sock_set_flag(ub->ubsock->sk, SOCK_DEAD);
sock_set_flag(ub->sk, SOCK_DEAD);
RCU_INIT_POINTER(ub->bearer, NULL);
/* sock_release need to be done outside of rtnl lock */
atomic_inc(&tipc_net(sock_net(ub->ubsock->sk))->wq_count);
atomic_inc(&tipc_net(sock_net(ub->sk))->wq_count);
INIT_WORK(&ub->work, cleanup_bearer);
schedule_work(&ub->work);
}