From: Guillaume Nault gnault@redhat.com
stable inclusion from linux-4.19.207 commit f94601e19dd7e80bf12f42a7cdfed9652a9a01dc
--------------------------------
commit 2dce224f469f060b9998a5a869151ef83c08ce77 upstream.
__peernet2id() can be protected by RCU as it only calls idr_for_each(), which is RCU-safe, and never modifies the nsid table.
rtnl_net_dumpid() can also do lockless lookups. It does two nested idr_for_each() calls on nsid tables (one direct call and one indirect call because of rtnl_net_dumpid_one() calling __peernet2id()). The netnsid tables are never updated. Therefore it is safe to not take the nsid_lock and run within an RCU-critical section instead.
Signed-off-by: Guillaume Nault gnault@redhat.com Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: HÃ¥kon Bugge haakon.bugge@oracle.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- net/core/net_namespace.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 5d997df949c56..594bbd7a676a3 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -206,9 +206,9 @@ static int net_eq_idr(int id, void *net, void *peer) return 0; }
-/* Should be called with nsid_lock held. If a new id is assigned, the bool alloc - * is set to true, thus the caller knows that the new id must be notified via - * rtnl. +/* Must be called from RCU-critical section or with nsid_lock held. If + * a new id is assigned, the bool alloc is set to true, thus the + * caller knows that the new id must be notified via rtnl. */ static int __peernet2id_alloc(struct net *net, struct net *peer, bool *alloc) { @@ -232,7 +232,7 @@ static int __peernet2id_alloc(struct net *net, struct net *peer, bool *alloc) return NETNSA_NSID_NOT_ASSIGNED; }
-/* should be called with nsid_lock held */ +/* Must be called from RCU-critical section or with nsid_lock held */ static int __peernet2id(struct net *net, struct net *peer) { bool no = false; @@ -275,9 +275,10 @@ int peernet2id(struct net *net, struct net *peer) { int id;
- spin_lock_bh(&net->nsid_lock); + rcu_read_lock(); id = __peernet2id(net, peer); - spin_unlock_bh(&net->nsid_lock); + rcu_read_unlock(); + return id; } EXPORT_SYMBOL(peernet2id); @@ -851,6 +852,7 @@ struct rtnl_net_dump_cb { int s_idx; };
+/* Runs in RCU-critical section. */ static int rtnl_net_dumpid_one(int id, void *peer, void *data) { struct rtnl_net_dump_cb *net_cb = (struct rtnl_net_dump_cb *)data; @@ -881,9 +883,9 @@ static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb) .s_idx = cb->args[0], };
- spin_lock_bh(&net->nsid_lock); + rcu_read_lock(); idr_for_each(&net->netns_ids, rtnl_net_dumpid_one, &net_cb); - spin_unlock_bh(&net->nsid_lock); + rcu_read_unlock();
cb->args[0] = net_cb.idx; return skb->len;