diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index dc0c081fc1f3..d58f99c67d4d 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -161,16 +161,26 @@ static void tcpm_check_stamp(struct tcp_metrics_block *tm, rcu_dereference_protected(p, lockdep_is_held(&tcp_metrics_lock)) static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, + struct net *net, struct inetpeer_addr *saddr, struct inetpeer_addr *daddr, unsigned int hash) { struct tcp_metrics_block *tm; bool reclaim = false; - struct net *net; spin_lock_bh(&tcp_metrics_lock); - net = dst_dev_net_rcu(dst); + + /* Do not add entries to a dismantling netns: they would not be + * flushed again. Callers are in an RCU read-side section, and + * cleanup_net() waits for a grace period after the netns refcount + * reached zero and before tcp_metrics_flush_all(NULL) runs, so an + * entry added after a successful check_net() is seen by the flush. + */ + if (unlikely(!check_net(net))) { + tm = NULL; + goto out_unlock; + } /* While waiting for the spin-lock the cache might have been populated * with this entry and so we have to check again. @@ -273,7 +283,7 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, return NULL; } - net = dst_dev_net_rcu(dst); + net = sock_net(req_to_sk(req)); hash ^= net_hash_mix(net); hash = hash_32(hash, tcp_metrics_hash_log); @@ -318,7 +328,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, else return NULL; - net = dst_dev_net_rcu(dst); + net = sock_net(sk); hash ^= net_hash_mix(net); hash = hash_32(hash, tcp_metrics_hash_log); @@ -326,7 +336,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, if (tm == TCP_METRICS_RECLAIM_PTR) tm = NULL; if (!tm && create) - tm = tcpm_new(dst, &saddr, &daddr, hash); + tm = tcpm_new(dst, net, &saddr, &daddr, hash); else tcpm_check_stamp(tm, dst);