| From fa417c7813cfb227d6f2a3173621d47345d99650 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 22 Feb 2024 10:50:14 +0000 |
| Subject: rtnetlink: change nlk->cb_mutex role |
| |
| From: Eric Dumazet <edumazet@google.com> |
| |
| [ Upstream commit e39951d965bf58b5aba7f61dc1140dcb8271af22 ] |
| |
| In commit af65bdfce98d ("[NETLINK]: Switch cb_lock spinlock |
| to mutex and allow to override it"), Patrick McHardy used |
| a common mutex to protect both nlk->cb and the dump() operations. |
| |
| The override is used for rtnl dumps, registered with |
| rntl_register() and rntl_register_module(). |
| |
| We want to be able to opt-out some dump() operations |
| to not acquire RTNL, so we need to protect nlk->cb |
| with a per socket mutex. |
| |
| This patch renames nlk->cb_def_mutex to nlk->nl_cb_mutex |
| |
| The optional pointer to the mutex used to protect dump() |
| call is stored in nlk->dump_cb_mutex |
| |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Stable-dep-of: 5be2062e3080 ("mpls: Handle error of rtnl_register_module().") |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/netlink/af_netlink.c | 32 ++++++++++++++++++-------------- |
| net/netlink/af_netlink.h | 5 +++-- |
| 2 files changed, 21 insertions(+), 16 deletions(-) |
| |
| diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c |
| index db49fc4d42cf7..e40376997f393 100644 |
| --- a/net/netlink/af_netlink.c |
| +++ b/net/netlink/af_netlink.c |
| @@ -636,7 +636,7 @@ static struct proto netlink_proto = { |
| }; |
| |
| static int __netlink_create(struct net *net, struct socket *sock, |
| - struct mutex *cb_mutex, int protocol, |
| + struct mutex *dump_cb_mutex, int protocol, |
| int kern) |
| { |
| struct sock *sk; |
| @@ -651,15 +651,11 @@ static int __netlink_create(struct net *net, struct socket *sock, |
| sock_init_data(sock, sk); |
| |
| nlk = nlk_sk(sk); |
| - if (cb_mutex) { |
| - nlk->cb_mutex = cb_mutex; |
| - } else { |
| - nlk->cb_mutex = &nlk->cb_def_mutex; |
| - mutex_init(nlk->cb_mutex); |
| - lockdep_set_class_and_name(nlk->cb_mutex, |
| + mutex_init(&nlk->nl_cb_mutex); |
| + lockdep_set_class_and_name(&nlk->nl_cb_mutex, |
| nlk_cb_mutex_keys + protocol, |
| nlk_cb_mutex_key_strings[protocol]); |
| - } |
| + nlk->dump_cb_mutex = dump_cb_mutex; |
| init_waitqueue_head(&nlk->wait); |
| |
| sk->sk_destruct = netlink_sock_destruct; |
| @@ -2211,7 +2207,7 @@ static int netlink_dump(struct sock *sk, bool lock_taken) |
| int alloc_size; |
| |
| if (!lock_taken) |
| - mutex_lock(nlk->cb_mutex); |
| + mutex_lock(&nlk->nl_cb_mutex); |
| if (!nlk->cb_running) { |
| err = -EINVAL; |
| goto errout_skb; |
| @@ -2263,14 +2259,22 @@ static int netlink_dump(struct sock *sk, bool lock_taken) |
| netlink_skb_set_owner_r(skb, sk); |
| |
| if (nlk->dump_done_errno > 0) { |
| + struct mutex *extra_mutex = nlk->dump_cb_mutex; |
| + |
| cb->extack = &extack; |
| + |
| + if (extra_mutex) |
| + mutex_lock(extra_mutex); |
| nlk->dump_done_errno = cb->dump(skb, cb); |
| + if (extra_mutex) |
| + mutex_unlock(extra_mutex); |
| + |
| cb->extack = NULL; |
| } |
| |
| if (nlk->dump_done_errno > 0 || |
| skb_tailroom(skb) < nlmsg_total_size(sizeof(nlk->dump_done_errno))) { |
| - mutex_unlock(nlk->cb_mutex); |
| + mutex_unlock(&nlk->nl_cb_mutex); |
| |
| if (sk_filter(sk, skb)) |
| kfree_skb(skb); |
| @@ -2304,13 +2308,13 @@ static int netlink_dump(struct sock *sk, bool lock_taken) |
| WRITE_ONCE(nlk->cb_running, false); |
| module = cb->module; |
| skb = cb->skb; |
| - mutex_unlock(nlk->cb_mutex); |
| + mutex_unlock(&nlk->nl_cb_mutex); |
| module_put(module); |
| consume_skb(skb); |
| return 0; |
| |
| errout_skb: |
| - mutex_unlock(nlk->cb_mutex); |
| + mutex_unlock(&nlk->nl_cb_mutex); |
| kfree_skb(skb); |
| return err; |
| } |
| @@ -2333,7 +2337,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, |
| } |
| |
| nlk = nlk_sk(sk); |
| - mutex_lock(nlk->cb_mutex); |
| + mutex_lock(&nlk->nl_cb_mutex); |
| /* A dump is in progress... */ |
| if (nlk->cb_running) { |
| ret = -EBUSY; |
| @@ -2384,7 +2388,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, |
| module_put(control->module); |
| error_unlock: |
| sock_put(sk); |
| - mutex_unlock(nlk->cb_mutex); |
| + mutex_unlock(&nlk->nl_cb_mutex); |
| error_free: |
| kfree_skb(skb); |
| return ret; |
| diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h |
| index 2145979b9986a..9751e29d4bbb9 100644 |
| --- a/net/netlink/af_netlink.h |
| +++ b/net/netlink/af_netlink.h |
| @@ -39,8 +39,9 @@ struct netlink_sock { |
| bool cb_running; |
| int dump_done_errno; |
| struct netlink_callback cb; |
| - struct mutex *cb_mutex; |
| - struct mutex cb_def_mutex; |
| + struct mutex nl_cb_mutex; |
| + |
| + struct mutex *dump_cb_mutex; |
| void (*netlink_rcv)(struct sk_buff *skb); |
| int (*netlink_bind)(struct net *net, int group); |
| void (*netlink_unbind)(struct net *net, int group); |
| -- |
| 2.43.0 |
| |