[Devel] [PATCH VZ10 4/8] ve/net: thread owning ve through copy_net_ns

Pavel Tikhomirov ptikhomirov at virtuozzo.com
Wed Apr 29 16:41:38 MSK 2026


Add new_ve parameter to copy_net_ns(). NULL preserves the existing
behaviour of taking current ve via get_exec_env().

This will be used to derive correct ownership of newly created net
namespace in case of simultaneous creation of new ve namespace and net
namespace, were we would like new net namespace having the new ve as an
owner.

While on it, move ve reference count handling from setup_net() to
copy_net_ns(), since we already handle dec/inc_netns_avail() up there.
This way we don't need to thread it also to setup_net(). That is
also consistent with how user_ns is threaded.

The init_net.owner_ve is now set explicitly to &ve0 in net_ns_init()
since setup_net() no longer does it for the boot-time call.

There is no change in behaviour yet: create_new_namespaces() still uses
NULL.

https://virtuozzo.atlassian.net/browse/VSTOR-129744
Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
Feature: ve: ve generic structures
---
 include/net/net_namespace.h |  7 +++++--
 kernel/nsproxy.c            |  3 ++-
 net/core/net_namespace.c    | 33 +++++++++++++++++++++++----------
 3 files changed, 30 insertions(+), 13 deletions(-)

diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index ed077747f710..dd0edb96f0ee 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -203,9 +203,11 @@ struct net {
 /* Init's network namespace */
 extern struct net init_net;
 
+struct ve_struct;
+
 #ifdef CONFIG_NET_NS
 struct net *copy_net_ns(unsigned long flags, struct user_namespace *user_ns,
-			struct net *old_net);
+			struct net *old_net, struct ve_struct *new_ve);
 
 void net_ns_get_ownership(const struct net *net, kuid_t *uid, kgid_t *gid);
 
@@ -218,7 +220,8 @@ struct net *get_net_ns_by_fd(int fd);
 #include <linux/sched.h>
 #include <linux/nsproxy.h>
 static inline struct net *copy_net_ns(unsigned long flags,
-	struct user_namespace *user_ns, struct net *old_net)
+	struct user_namespace *user_ns, struct net *old_net,
+	struct ve_struct *new_ve)
 {
 	if (flags & CLONE_NEWNET)
 		return ERR_PTR(-EINVAL);
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index 12de23ec80bf..1d0e8f9d98a3 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -109,7 +109,8 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
 		goto out_cgroup;
 	}
 
-	new_nsp->net_ns = copy_net_ns(flags, user_ns, tsk->nsproxy->net_ns);
+	new_nsp->net_ns = copy_net_ns(flags, user_ns, tsk->nsproxy->net_ns,
+				      NULL);
 	if (IS_ERR(new_nsp->net_ns)) {
 		err = PTR_ERR(new_nsp->net_ns);
 		goto out_net;
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 40e74d956bc2..3738e5274cd9 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -352,10 +352,6 @@ static __net_init int setup_net(struct net *net)
 	LIST_HEAD(dev_kill_list);
 	int error = 0;
 
-#ifdef CONFIG_VE
-	net->owner_ve = get_ve(get_exec_env());
-#endif
-
 	preempt_disable();
 	net->net_cookie = gen_cookie_next(&net_cookie);
 	preempt_enable();
@@ -400,9 +396,6 @@ static __net_init int setup_net(struct net *net)
 		ops_free_list(ops, &net_exit_list);
 
 	rcu_barrier();
-#ifdef CONFIG_VE
-	put_ve(net->owner_ve);
-#endif
 	goto out;
 }
 
@@ -505,9 +498,10 @@ static void inc_netns_avail(struct ve_struct *ve)
 #endif
 
 struct net *copy_net_ns(unsigned long flags,
-			struct user_namespace *user_ns, struct net *old_net)
+			struct user_namespace *user_ns, struct net *old_net,
+			struct ve_struct *new_ve)
 {
-	struct ve_struct *ve = get_exec_env();
+	struct ve_struct *ve;
 	struct ucounts *ucounts;
 	struct net *net;
 	int rv;
@@ -515,6 +509,14 @@ struct net *copy_net_ns(unsigned long flags,
 	if (!(flags & CLONE_NEWNET))
 		return get_net(old_net);
 
+	/*
+	 * The ve that should own the new netns. When called from
+	 * copy_namespaces()/unshare_nsproxy_namespaces() with a freshly
+	 * created CLONE_NEWVE in flight, @new_ve is supplied by the
+	 * caller because get_exec_env() still resolves to the parent ve.
+	 */
+	ve = new_ve ?: get_exec_env();
+
 	ucounts = inc_net_namespaces(user_ns);
 	if (!ucounts)
 		return ERR_PTR(-ENOSPC);
@@ -538,16 +540,23 @@ struct net *copy_net_ns(unsigned long flags,
 	preinit_net(net, user_ns);
 	net->ucounts = ucounts;
 	get_user_ns(user_ns);
+#ifdef CONFIG_VE
+	net->owner_ve = get_ve(ve);
+#endif
 
 	rv = down_read_killable(&pernet_ops_rwsem);
 	if (rv < 0)
-		goto put_userns;
+		goto put_owner_ve;
 
 	rv = setup_net(net);
 
 	up_read(&pernet_ops_rwsem);
 
 	if (rv < 0) {
+put_owner_ve:
+#ifdef CONFIG_VE
+		put_ve(net->owner_ve);
+#endif
 put_userns:
 #ifdef CONFIG_KEYS
 		key_remove_domain(net->key_domain);
@@ -1261,6 +1270,10 @@ void __init net_ns_init(void)
 #endif
 	preinit_net(&init_net, &init_user_ns);
 
+#ifdef CONFIG_VE
+	init_net.owner_ve = get_ve(&ve0);
+#endif
+
 	down_write(&pernet_ops_rwsem);
 	if (setup_net(&init_net))
 		panic("Could not setup the initial network namespace");
-- 
2.53.0



More information about the Devel mailing list