diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 0f5eb9db0c6264efc1ac83ab577511fd6823f4fe..59dbeff84b40afdfefe8e12b4421ba3a940d09e0 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -296,6 +296,7 @@ static inline int check_net(const struct net *net) return refcount_read(&net->ns.count) != 0; } +void netns_passive_drop(struct net *net); void net_drop_ns(void *); #else @@ -325,6 +326,11 @@ static inline int check_net(const struct net *net) return 1; } +static inline void netns_passive_drop(struct net *net) +{ + refcount_dec(&net->passive); +} + #define net_drop_ns NULL #endif diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index cb39a12b2f8295c605f08b5589932932150a1644..dd58056c2800e25106f3275c907b20d92906af21 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -464,7 +464,7 @@ static void net_complete_free(void) } -static void net_free(struct net *net) +void netns_passive_drop(struct net *net) { if (refcount_dec_and_test(&net->passive)) { kfree(rcu_access_pointer(net->gen)); @@ -482,7 +482,7 @@ void net_drop_ns(void *p) struct net *net = (struct net *)p; if (net) - net_free(net); + netns_passive_drop(net); } struct net *copy_net_ns(unsigned long flags, @@ -523,7 +523,7 @@ struct net *copy_net_ns(unsigned long flags, key_remove_domain(net->key_domain); #endif put_user_ns(user_ns); - net_free(net); + netns_passive_drop(net); dec_ucounts: dec_net_namespaces(ucounts); return ERR_PTR(rv); @@ -672,7 +672,7 @@ static void cleanup_net(struct work_struct *work) key_remove_domain(net->key_domain); #endif put_user_ns(net->user_ns); - net_free(net); + netns_passive_drop(net); } cleanup_net_task = NULL; } diff --git a/net/core/sock.c b/net/core/sock.c index eae2ae70a2e03df370d8ef7750a7bb13cc3b8d8f..cf9f80d03161afe55c4695ac3fb446e9e787315a 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2246,6 +2246,7 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority, get_net_track(net, &sk->ns_tracker, priority); sock_inuse_add(net, 1); } else { + refcount_inc(&net->passive); __netns_tracker_alloc(net, &sk->ns_tracker, false, priority); } @@ -2270,6 +2271,7 @@ EXPORT_SYMBOL(sk_alloc); static void __sk_destruct(struct rcu_head *head) { struct sock *sk = container_of(head, struct sock, sk_rcu); + struct net *net = sock_net(sk); struct sk_filter *filter; if (sk->sk_destruct) @@ -2301,11 +2303,12 @@ static void __sk_destruct(struct rcu_head *head) put_cred(sk->sk_peer_cred); put_pid(sk->sk_peer_pid); - if (likely(sk->sk_net_refcnt)) - put_net_track(sock_net(sk), &sk->ns_tracker); - else - __netns_tracker_free(sock_net(sk), &sk->ns_tracker, false); - + if (likely(sk->sk_net_refcnt)) { + put_net_track(net, &sk->ns_tracker); + } else { + __netns_tracker_free(net, &sk->ns_tracker, false); + netns_passive_drop(net); + } sk_prot_free(sk->sk_prot_creator, sk); } @@ -2405,6 +2408,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) * is not properly dismantling its kernel sockets at netns * destroy time. */ + refcount_inc(&sock_net(newsk)->passive); __netns_tracker_alloc(sock_net(newsk), &newsk->ns_tracker, false, priority); }