summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKirill Tkhai <ktkhai@virtuozzo.com>2018-01-12 10:28:31 -0500
committerDavid S. Miller <davem@davemloft.net>2018-01-15 14:23:42 -0500
commit273c28bc57ca9672f7b70bed764ecdfb964930c8 (patch)
tree99fda63f67f8e081924a6d39af93dfbbb04e6eff
parent594831a8aba3fd045c3212a3e3bb9788c77b989d (diff)
net: Convert atomic_t net::count to refcount_t
Since net could be obtained from RCU lists, and there is a race with net destruction, the patch converts net::count to refcount_t. This provides sanity checks for the cases of incrementing counter of already dead net, when maybe_get_net() has to used instead of get_net(). Drivers: allyesconfig and allmodconfig are OK. Suggested-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/net_namespace.h8
-rw-r--r--net/core/net-sysfs.c6
-rw-r--r--net/core/net_namespace.c8
-rw-r--r--net/ipv4/inet_timewait_sock.c4
-rw-r--r--net/ipv4/tcp_metrics.c2
5 files changed, 14 insertions, 14 deletions
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 10f99dafd5ac..f8a84a2c2341 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -51,7 +51,7 @@ struct net {
51 refcount_t passive; /* To decided when the network 51 refcount_t passive; /* To decided when the network
52 * namespace should be freed. 52 * namespace should be freed.
53 */ 53 */
54 atomic_t count; /* To decided when the network 54 refcount_t count; /* To decided when the network
55 * namespace should be shut down. 55 * namespace should be shut down.
56 */ 56 */
57 spinlock_t rules_mod_lock; 57 spinlock_t rules_mod_lock;
@@ -195,7 +195,7 @@ void __put_net(struct net *net);
195 195
196static inline struct net *get_net(struct net *net) 196static inline struct net *get_net(struct net *net)
197{ 197{
198 atomic_inc(&net->count); 198 refcount_inc(&net->count);
199 return net; 199 return net;
200} 200}
201 201
@@ -206,14 +206,14 @@ static inline struct net *maybe_get_net(struct net *net)
206 * exists. If the reference count is zero this 206 * exists. If the reference count is zero this
207 * function fails and returns NULL. 207 * function fails and returns NULL.
208 */ 208 */
209 if (!atomic_inc_not_zero(&net->count)) 209 if (!refcount_inc_not_zero(&net->count))
210 net = NULL; 210 net = NULL;
211 return net; 211 return net;
212} 212}
213 213
214static inline void put_net(struct net *net) 214static inline void put_net(struct net *net)
215{ 215{
216 if (atomic_dec_and_test(&net->count)) 216 if (refcount_dec_and_test(&net->count))
217 __put_net(net); 217 __put_net(net);
218} 218}
219 219
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 799b75268291..7bf8b85ade16 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -961,7 +961,7 @@ net_rx_queue_update_kobjects(struct net_device *dev, int old_num, int new_num)
961 while (--i >= new_num) { 961 while (--i >= new_num) {
962 struct kobject *kobj = &dev->_rx[i].kobj; 962 struct kobject *kobj = &dev->_rx[i].kobj;
963 963
964 if (!atomic_read(&dev_net(dev)->count)) 964 if (!refcount_read(&dev_net(dev)->count))
965 kobj->uevent_suppress = 1; 965 kobj->uevent_suppress = 1;
966 if (dev->sysfs_rx_queue_group) 966 if (dev->sysfs_rx_queue_group)
967 sysfs_remove_group(kobj, dev->sysfs_rx_queue_group); 967 sysfs_remove_group(kobj, dev->sysfs_rx_queue_group);
@@ -1367,7 +1367,7 @@ netdev_queue_update_kobjects(struct net_device *dev, int old_num, int new_num)
1367 while (--i >= new_num) { 1367 while (--i >= new_num) {
1368 struct netdev_queue *queue = dev->_tx + i; 1368 struct netdev_queue *queue = dev->_tx + i;
1369 1369
1370 if (!atomic_read(&dev_net(dev)->count)) 1370 if (!refcount_read(&dev_net(dev)->count))
1371 queue->kobj.uevent_suppress = 1; 1371 queue->kobj.uevent_suppress = 1;
1372#ifdef CONFIG_BQL 1372#ifdef CONFIG_BQL
1373 sysfs_remove_group(&queue->kobj, &dql_group); 1373 sysfs_remove_group(&queue->kobj, &dql_group);
@@ -1558,7 +1558,7 @@ void netdev_unregister_kobject(struct net_device *ndev)
1558{ 1558{
1559 struct device *dev = &ndev->dev; 1559 struct device *dev = &ndev->dev;
1560 1560
1561 if (!atomic_read(&dev_net(ndev)->count)) 1561 if (!refcount_read(&dev_net(ndev)->count))
1562 dev_set_uevent_suppress(dev, 1); 1562 dev_set_uevent_suppress(dev, 1);
1563 1563
1564 kobject_get(&dev->kobj); 1564 kobject_get(&dev->kobj);
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 60a71be75aea..2213d45fcafd 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -35,7 +35,7 @@ LIST_HEAD(net_namespace_list);
35EXPORT_SYMBOL_GPL(net_namespace_list); 35EXPORT_SYMBOL_GPL(net_namespace_list);
36 36
37struct net init_net = { 37struct net init_net = {
38 .count = ATOMIC_INIT(1), 38 .count = REFCOUNT_INIT(1),
39 .dev_base_head = LIST_HEAD_INIT(init_net.dev_base_head), 39 .dev_base_head = LIST_HEAD_INIT(init_net.dev_base_head),
40}; 40};
41EXPORT_SYMBOL(init_net); 41EXPORT_SYMBOL(init_net);
@@ -224,10 +224,10 @@ int peernet2id_alloc(struct net *net, struct net *peer)
224 bool alloc; 224 bool alloc;
225 int id; 225 int id;
226 226
227 if (atomic_read(&net->count) == 0) 227 if (refcount_read(&net->count) == 0)
228 return NETNSA_NSID_NOT_ASSIGNED; 228 return NETNSA_NSID_NOT_ASSIGNED;
229 spin_lock_bh(&net->nsid_lock); 229 spin_lock_bh(&net->nsid_lock);
230 alloc = atomic_read(&peer->count) == 0 ? false : true; 230 alloc = refcount_read(&peer->count) == 0 ? false : true;
231 id = __peernet2id_alloc(net, peer, &alloc); 231 id = __peernet2id_alloc(net, peer, &alloc);
232 spin_unlock_bh(&net->nsid_lock); 232 spin_unlock_bh(&net->nsid_lock);
233 if (alloc && id >= 0) 233 if (alloc && id >= 0)
@@ -284,7 +284,7 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
284 int error = 0; 284 int error = 0;
285 LIST_HEAD(net_exit_list); 285 LIST_HEAD(net_exit_list);
286 286
287 atomic_set(&net->count, 1); 287 refcount_set(&net->count, 1);
288 refcount_set(&net->passive, 1); 288 refcount_set(&net->passive, 1);
289 net->dev_base_seq = 1; 289 net->dev_base_seq = 1;
290 net->user_ns = user_ns; 290 net->user_ns = user_ns;
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index 277ff69a312d..c3ea4906d237 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -270,14 +270,14 @@ restart:
270 continue; 270 continue;
271 tw = inet_twsk(sk); 271 tw = inet_twsk(sk);
272 if ((tw->tw_family != family) || 272 if ((tw->tw_family != family) ||
273 atomic_read(&twsk_net(tw)->count)) 273 refcount_read(&twsk_net(tw)->count))
274 continue; 274 continue;
275 275
276 if (unlikely(!refcount_inc_not_zero(&tw->tw_refcnt))) 276 if (unlikely(!refcount_inc_not_zero(&tw->tw_refcnt)))
277 continue; 277 continue;
278 278
279 if (unlikely((tw->tw_family != family) || 279 if (unlikely((tw->tw_family != family) ||
280 atomic_read(&twsk_net(tw)->count))) { 280 refcount_read(&twsk_net(tw)->count))) {
281 inet_twsk_put(tw); 281 inet_twsk_put(tw);
282 goto restart; 282 goto restart;
283 } 283 }
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index 759e6bc8327b..03b51cdcc731 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -892,7 +892,7 @@ static void tcp_metrics_flush_all(struct net *net)
892 pp = &hb->chain; 892 pp = &hb->chain;
893 for (tm = deref_locked(*pp); tm; tm = deref_locked(*pp)) { 893 for (tm = deref_locked(*pp); tm; tm = deref_locked(*pp)) {
894 match = net ? net_eq(tm_net(tm), net) : 894 match = net ? net_eq(tm_net(tm), net) :
895 !atomic_read(&tm_net(tm)->count); 895 !refcount_read(&tm_net(tm)->count);
896 if (match) { 896 if (match) {
897 *pp = tm->tcpm_next; 897 *pp = tm->tcpm_next;
898 kfree_rcu(tm, rcu_head); 898 kfree_rcu(tm, rcu_head);