All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next 0/3] netns: Optimise netns ID lookups
@ 2020-01-13 21:39 Guillaume Nault
  2020-01-13 21:39 ` [PATCH net-next 1/3] netns: Remove __peernet2id_alloc() Guillaume Nault
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Guillaume Nault @ 2020-01-13 21:39 UTC (permalink / raw)
  To: David Miller, Jakub Kicinski; +Cc: netdev, Nicolas Dichtel

Netns ID lookups can be easily protected by RCU, rather than by holding
a spinlock.

Patch 1 prepares the code, patch 2 does the RCU conversion, and finally
patch 3 stops disabling BHs on updates (patch 2 makes that unnecessary).

Guillaume Nault (3):
  netns: Remove __peernet2id_alloc()
  netns: protect netns ID lookups with RCU
  netns: don't disable BHs when locking "nsid_lock"

 net/core/net_namespace.c | 93 ++++++++++++++++------------------------
 1 file changed, 38 insertions(+), 55 deletions(-)

-- 
2.21.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH net-next 1/3] netns: Remove __peernet2id_alloc()
  2020-01-13 21:39 [PATCH net-next 0/3] netns: Optimise netns ID lookups Guillaume Nault
@ 2020-01-13 21:39 ` Guillaume Nault
  2020-01-13 21:39 ` [PATCH net-next 2/3] netns: protect netns ID lookups with RCU Guillaume Nault
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Guillaume Nault @ 2020-01-13 21:39 UTC (permalink / raw)
  To: David Miller, Jakub Kicinski; +Cc: netdev, Nicolas Dichtel

__peernet2id_alloc() was used for both plain lookups and for netns ID
allocations (depending the value of '*alloc'). Let's separate lookups
from allocations instead. That is, integrate the lookup code into
__peernet2id() and make peernet2id_alloc() responsible for allocating
new netns IDs when necessary.

This makes it clear that __peernet2id() doesn't modify the idr and
prepares the code for lockless lookups.

Also, mark the 'net' argument of __peernet2id() as 'const', since we're
modifying this line.

Signed-off-by: Guillaume Nault <gnault@redhat.com>
---
 net/core/net_namespace.c | 55 +++++++++++++++++-----------------------
 1 file changed, 23 insertions(+), 32 deletions(-)

diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 39402840025e..05e07d24b45b 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -211,16 +211,10 @@ 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.
- */
-static int __peernet2id_alloc(struct net *net, struct net *peer, bool *alloc)
+/* Should be called with nsid_lock held. */
+static int __peernet2id(const struct net *net, struct net *peer)
 {
 	int id = idr_for_each(&net->netns_ids, net_eq_idr, peer);
-	bool alloc_it = *alloc;
-
-	*alloc = false;
 
 	/* Magic value for id 0. */
 	if (id == NET_ID_ZERO)
@@ -228,23 +222,9 @@ static int __peernet2id_alloc(struct net *net, struct net *peer, bool *alloc)
 	if (id > 0)
 		return id;
 
-	if (alloc_it) {
-		id = alloc_netid(net, peer, -1);
-		*alloc = true;
-		return id >= 0 ? id : NETNSA_NSID_NOT_ASSIGNED;
-	}
-
 	return NETNSA_NSID_NOT_ASSIGNED;
 }
 
-/* should be called with nsid_lock held */
-static int __peernet2id(struct net *net, struct net *peer)
-{
-	bool no = false;
-
-	return __peernet2id_alloc(net, peer, &no);
-}
-
 static void rtnl_net_notifyid(struct net *net, int cmd, int id, u32 portid,
 			      struct nlmsghdr *nlh, gfp_t gfp);
 /* This function returns the id of a peer netns. If no id is assigned, one will
@@ -252,26 +232,37 @@ static void rtnl_net_notifyid(struct net *net, int cmd, int id, u32 portid,
  */
 int peernet2id_alloc(struct net *net, struct net *peer, gfp_t gfp)
 {
-	bool alloc = false, alive = false;
 	int id;
 
 	if (refcount_read(&net->count) == 0)
 		return NETNSA_NSID_NOT_ASSIGNED;
+
 	spin_lock_bh(&net->nsid_lock);
-	/*
-	 * When peer is obtained from RCU lists, we may race with
+	id = __peernet2id(net, peer);
+	if (id >= 0) {
+		spin_unlock_bh(&net->nsid_lock);
+		return id;
+	}
+
+	/* When peer is obtained from RCU lists, we may race with
 	 * its cleanup. Check whether it's alive, and this guarantees
 	 * we never hash a peer back to net->netns_ids, after it has
 	 * just been idr_remove()'d from there in cleanup_net().
 	 */
-	if (maybe_get_net(peer))
-		alive = alloc = true;
-	id = __peernet2id_alloc(net, peer, &alloc);
+	if (!maybe_get_net(peer)) {
+		spin_unlock_bh(&net->nsid_lock);
+		return NETNSA_NSID_NOT_ASSIGNED;
+	}
+
+	id = alloc_netid(net, peer, -1);
 	spin_unlock_bh(&net->nsid_lock);
-	if (alloc && id >= 0)
-		rtnl_net_notifyid(net, RTM_NEWNSID, id, 0, NULL, gfp);
-	if (alive)
-		put_net(peer);
+
+	put_net(peer);
+	if (id < 0)
+		return NETNSA_NSID_NOT_ASSIGNED;
+
+	rtnl_net_notifyid(net, RTM_NEWNSID, id, 0, NULL, gfp);
+
 	return id;
 }
 EXPORT_SYMBOL_GPL(peernet2id_alloc);
-- 
2.21.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH net-next 2/3] netns: protect netns ID lookups with RCU
  2020-01-13 21:39 [PATCH net-next 0/3] netns: Optimise netns ID lookups Guillaume Nault
  2020-01-13 21:39 ` [PATCH net-next 1/3] netns: Remove __peernet2id_alloc() Guillaume Nault
@ 2020-01-13 21:39 ` Guillaume Nault
  2020-01-13 21:39 ` [PATCH net-next 3/3] netns: don't disable BHs when locking "nsid_lock" Guillaume Nault
  2020-01-14 19:29 ` [PATCH net-next 0/3] netns: Optimise netns ID lookups David Miller
  3 siblings, 0 replies; 5+ messages in thread
From: Guillaume Nault @ 2020-01-13 21:39 UTC (permalink / raw)
  To: David Miller, Jakub Kicinski; +Cc: netdev, Nicolas Dichtel

__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>
---
 net/core/net_namespace.c | 22 +++++++---------------
 1 file changed, 7 insertions(+), 15 deletions(-)

diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 05e07d24b45b..e7a5ff4966c9 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -211,7 +211,7 @@ static int net_eq_idr(int id, void *net, void *peer)
 	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)
 {
 	int id = idr_for_each(&net->netns_ids, net_eq_idr, peer);
@@ -272,9 +272,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);
@@ -941,6 +942,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;
@@ -1025,19 +1027,9 @@ static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb)
 			goto end;
 	}
 
-	spin_lock_bh(&net_cb.tgt_net->nsid_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;
-	}
+	rcu_read_lock();
 	idr_for_each(&net_cb.tgt_net->netns_ids, rtnl_net_dumpid_one, &net_cb);
-	if (net_cb.fillargs.add_ref &&
-	    !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);
+	rcu_read_unlock();
 
 	cb->args[0] = net_cb.idx;
 end:
-- 
2.21.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH net-next 3/3] netns: don't disable BHs when locking "nsid_lock"
  2020-01-13 21:39 [PATCH net-next 0/3] netns: Optimise netns ID lookups Guillaume Nault
  2020-01-13 21:39 ` [PATCH net-next 1/3] netns: Remove __peernet2id_alloc() Guillaume Nault
  2020-01-13 21:39 ` [PATCH net-next 2/3] netns: protect netns ID lookups with RCU Guillaume Nault
@ 2020-01-13 21:39 ` Guillaume Nault
  2020-01-14 19:29 ` [PATCH net-next 0/3] netns: Optimise netns ID lookups David Miller
  3 siblings, 0 replies; 5+ messages in thread
From: Guillaume Nault @ 2020-01-13 21:39 UTC (permalink / raw)
  To: David Miller, Jakub Kicinski; +Cc: netdev, Nicolas Dichtel

When peernet2id() had to lock "nsid_lock" before iterating through the
nsid table, we had to disable BHs, because VXLAN can call peernet2id()
from the xmit path:
  vxlan_xmit() -> vxlan_fdb_miss() -> vxlan_fdb_notify()
    -> __vxlan_fdb_notify() -> vxlan_fdb_info() -> peernet2id().

Now that peernet2id() uses RCU protection, "nsid_lock" isn't used in BH
context anymore. Therefore, we can safely use plain
spin_lock()/spin_unlock() and let BHs run when holding "nsid_lock".

Signed-off-by: Guillaume Nault <gnault@redhat.com>
---
 net/core/net_namespace.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index e7a5ff4966c9..6412c1fbfcb5 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -237,10 +237,10 @@ int peernet2id_alloc(struct net *net, struct net *peer, gfp_t gfp)
 	if (refcount_read(&net->count) == 0)
 		return NETNSA_NSID_NOT_ASSIGNED;
 
-	spin_lock_bh(&net->nsid_lock);
+	spin_lock(&net->nsid_lock);
 	id = __peernet2id(net, peer);
 	if (id >= 0) {
-		spin_unlock_bh(&net->nsid_lock);
+		spin_unlock(&net->nsid_lock);
 		return id;
 	}
 
@@ -250,12 +250,12 @@ int peernet2id_alloc(struct net *net, struct net *peer, gfp_t gfp)
 	 * just been idr_remove()'d from there in cleanup_net().
 	 */
 	if (!maybe_get_net(peer)) {
-		spin_unlock_bh(&net->nsid_lock);
+		spin_unlock(&net->nsid_lock);
 		return NETNSA_NSID_NOT_ASSIGNED;
 	}
 
 	id = alloc_netid(net, peer, -1);
-	spin_unlock_bh(&net->nsid_lock);
+	spin_unlock(&net->nsid_lock);
 
 	put_net(peer);
 	if (id < 0)
@@ -520,20 +520,20 @@ static void unhash_nsid(struct net *net, struct net *last)
 	for_each_net(tmp) {
 		int id;
 
-		spin_lock_bh(&tmp->nsid_lock);
+		spin_lock(&tmp->nsid_lock);
 		id = __peernet2id(tmp, net);
 		if (id >= 0)
 			idr_remove(&tmp->netns_ids, id);
-		spin_unlock_bh(&tmp->nsid_lock);
+		spin_unlock(&tmp->nsid_lock);
 		if (id >= 0)
 			rtnl_net_notifyid(tmp, RTM_DELNSID, id, 0, NULL,
 					  GFP_KERNEL);
 		if (tmp == last)
 			break;
 	}
-	spin_lock_bh(&net->nsid_lock);
+	spin_lock(&net->nsid_lock);
 	idr_destroy(&net->netns_ids);
-	spin_unlock_bh(&net->nsid_lock);
+	spin_unlock(&net->nsid_lock);
 }
 
 static LLIST_HEAD(cleanup_list);
@@ -746,9 +746,9 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh,
 		return PTR_ERR(peer);
 	}
 
-	spin_lock_bh(&net->nsid_lock);
+	spin_lock(&net->nsid_lock);
 	if (__peernet2id(net, peer) >= 0) {
-		spin_unlock_bh(&net->nsid_lock);
+		spin_unlock(&net->nsid_lock);
 		err = -EEXIST;
 		NL_SET_BAD_ATTR(extack, nla);
 		NL_SET_ERR_MSG(extack,
@@ -757,7 +757,7 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh,
 	}
 
 	err = alloc_netid(net, peer, nsid);
-	spin_unlock_bh(&net->nsid_lock);
+	spin_unlock(&net->nsid_lock);
 	if (err >= 0) {
 		rtnl_net_notifyid(net, RTM_NEWNSID, err, NETLINK_CB(skb).portid,
 				  nlh, GFP_KERNEL);
-- 
2.21.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH net-next 0/3] netns: Optimise netns ID lookups
  2020-01-13 21:39 [PATCH net-next 0/3] netns: Optimise netns ID lookups Guillaume Nault
                   ` (2 preceding siblings ...)
  2020-01-13 21:39 ` [PATCH net-next 3/3] netns: don't disable BHs when locking "nsid_lock" Guillaume Nault
@ 2020-01-14 19:29 ` David Miller
  3 siblings, 0 replies; 5+ messages in thread
From: David Miller @ 2020-01-14 19:29 UTC (permalink / raw)
  To: gnault; +Cc: jakub.kicinski, netdev, nicolas.dichtel

From: Guillaume Nault <gnault@redhat.com>
Date: Mon, 13 Jan 2020 22:39:19 +0100

> Netns ID lookups can be easily protected by RCU, rather than by holding
> a spinlock.
> 
> Patch 1 prepares the code, patch 2 does the RCU conversion, and finally
> patch 3 stops disabling BHs on updates (patch 2 makes that unnecessary).

Series applied, thanks.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-01-14 19:29 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-13 21:39 [PATCH net-next 0/3] netns: Optimise netns ID lookups Guillaume Nault
2020-01-13 21:39 ` [PATCH net-next 1/3] netns: Remove __peernet2id_alloc() Guillaume Nault
2020-01-13 21:39 ` [PATCH net-next 2/3] netns: protect netns ID lookups with RCU Guillaume Nault
2020-01-13 21:39 ` [PATCH net-next 3/3] netns: don't disable BHs when locking "nsid_lock" Guillaume Nault
2020-01-14 19:29 ` [PATCH net-next 0/3] netns: Optimise netns ID lookups David Miller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.