Commit 2dce224f authored by Guillaume Nault's avatar Guillaume Nault Committed by David S. Miller

netns: protect netns ID lookups with RCU

__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: default avatarGuillaume Nault <gnault@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 49052941
...@@ -211,7 +211,7 @@ static int net_eq_idr(int id, void *net, void *peer) ...@@ -211,7 +211,7 @@ static int net_eq_idr(int id, void *net, void *peer)
return 0; return 0;
} }
/* Should be called with nsid_lock held. */ /* Must be called from RCU-critical section or with nsid_lock held */
static int __peernet2id(const struct net *net, struct net *peer) static int __peernet2id(const struct net *net, struct net *peer)
{ {
int id = idr_for_each(&net->netns_ids, net_eq_idr, peer); int id = idr_for_each(&net->netns_ids, net_eq_idr, peer);
...@@ -272,9 +272,10 @@ int peernet2id(struct net *net, struct net *peer) ...@@ -272,9 +272,10 @@ int peernet2id(struct net *net, struct net *peer)
{ {
int id; int id;
spin_lock_bh(&net->nsid_lock); rcu_read_lock();
id = __peernet2id(net, peer); id = __peernet2id(net, peer);
spin_unlock_bh(&net->nsid_lock); rcu_read_unlock();
return id; return id;
} }
EXPORT_SYMBOL(peernet2id); EXPORT_SYMBOL(peernet2id);
...@@ -941,6 +942,7 @@ struct rtnl_net_dump_cb { ...@@ -941,6 +942,7 @@ struct rtnl_net_dump_cb {
int s_idx; int s_idx;
}; };
/* Runs in RCU-critical section. */
static int rtnl_net_dumpid_one(int id, void *peer, void *data) 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; struct rtnl_net_dump_cb *net_cb = (struct rtnl_net_dump_cb *)data;
...@@ -1025,19 +1027,9 @@ static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1025,19 +1027,9 @@ static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb)
goto end; goto end;
} }
spin_lock_bh(&net_cb.tgt_net->nsid_lock); rcu_read_lock();
if (net_cb.fillargs.add_ref &&
!net_eq(net_cb.ref_net, net_cb.tgt_net) &&
!spin_trylock_bh(&net_cb.ref_net->nsid_lock)) {
spin_unlock_bh(&net_cb.tgt_net->nsid_lock);
err = -EAGAIN;
goto end;
}
idr_for_each(&net_cb.tgt_net->netns_ids, rtnl_net_dumpid_one, &net_cb); idr_for_each(&net_cb.tgt_net->netns_ids, rtnl_net_dumpid_one, &net_cb);
if (net_cb.fillargs.add_ref && rcu_read_unlock();
!net_eq(net_cb.ref_net, net_cb.tgt_net))
spin_unlock_bh(&net_cb.ref_net->nsid_lock);
spin_unlock_bh(&net_cb.tgt_net->nsid_lock);
cb->args[0] = net_cb.idx; cb->args[0] = net_cb.idx;
end: end:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment