diff options
| author | Jesper Dangaard Brouer <brouer@redhat.com> | 2014-03-03 08:45:20 -0500 |
|---|---|---|
| committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2014-03-07 05:40:38 -0500 |
| commit | b7779d06f9950e14a008a2de970b44233fe49c86 (patch) | |
| tree | 2044ec0cd420ca28f764806f41055e0c6ab33d99 | |
| parent | b476b72a0f8514a5a4c561bab731ddd506a284e7 (diff) | |
netfilter: conntrack: spinlock per cpu to protect special lists.
One spinlock per cpu to protect dying/unconfirmed/template special lists.
(These lists are now per cpu, a bit like the untracked ct)
Add a @cpu field to nf_conn, to make sure we hold the appropriate
spinlock at removal time.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Reviewed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
| -rw-r--r-- | include/net/netfilter/nf_conntrack.h | 3 | ||||
| -rw-r--r-- | include/net/netns/conntrack.h | 11 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_core.c | 141 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_helper.c | 11 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_netlink.c | 81 |
5 files changed, 168 insertions, 79 deletions
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index e10d1faa6d09..37252f71a380 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h | |||
| @@ -82,7 +82,8 @@ struct nf_conn { | |||
| 82 | */ | 82 | */ |
| 83 | struct nf_conntrack ct_general; | 83 | struct nf_conntrack ct_general; |
| 84 | 84 | ||
| 85 | spinlock_t lock; | 85 | spinlock_t lock; |
| 86 | u16 cpu; | ||
| 86 | 87 | ||
| 87 | /* XXX should I move this to the tail ? - Y.K */ | 88 | /* XXX should I move this to the tail ? - Y.K */ |
| 88 | /* These are my tuples; original and reply */ | 89 | /* These are my tuples; original and reply */ |
diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index fbcc7fa536dc..c6a8994e9922 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h | |||
| @@ -62,6 +62,13 @@ struct nf_ip_net { | |||
| 62 | #endif | 62 | #endif |
| 63 | }; | 63 | }; |
| 64 | 64 | ||
| 65 | struct ct_pcpu { | ||
| 66 | spinlock_t lock; | ||
| 67 | struct hlist_nulls_head unconfirmed; | ||
| 68 | struct hlist_nulls_head dying; | ||
| 69 | struct hlist_nulls_head tmpl; | ||
| 70 | }; | ||
| 71 | |||
| 65 | struct netns_ct { | 72 | struct netns_ct { |
| 66 | atomic_t count; | 73 | atomic_t count; |
| 67 | unsigned int expect_count; | 74 | unsigned int expect_count; |
| @@ -86,9 +93,7 @@ struct netns_ct { | |||
| 86 | struct kmem_cache *nf_conntrack_cachep; | 93 | struct kmem_cache *nf_conntrack_cachep; |
| 87 | struct hlist_nulls_head *hash; | 94 | struct hlist_nulls_head *hash; |
| 88 | struct hlist_head *expect_hash; | 95 | struct hlist_head *expect_hash; |
| 89 | struct hlist_nulls_head unconfirmed; | 96 | struct ct_pcpu __percpu *pcpu_lists; |
| 90 | struct hlist_nulls_head dying; | ||
| 91 | struct hlist_nulls_head tmpl; | ||
| 92 | struct ip_conntrack_stat __percpu *stat; | 97 | struct ip_conntrack_stat __percpu *stat; |
| 93 | struct nf_ct_event_notifier __rcu *nf_conntrack_event_cb; | 98 | struct nf_ct_event_notifier __rcu *nf_conntrack_event_cb; |
| 94 | struct nf_exp_event_notifier __rcu *nf_expect_event_cb; | 99 | struct nf_exp_event_notifier __rcu *nf_expect_event_cb; |
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 965693eb1f0e..289b27901d8c 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c | |||
| @@ -192,6 +192,50 @@ clean_from_lists(struct nf_conn *ct) | |||
| 192 | nf_ct_remove_expectations(ct); | 192 | nf_ct_remove_expectations(ct); |
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | /* must be called with local_bh_disable */ | ||
| 196 | static void nf_ct_add_to_dying_list(struct nf_conn *ct) | ||
| 197 | { | ||
| 198 | struct ct_pcpu *pcpu; | ||
| 199 | |||
| 200 | /* add this conntrack to the (per cpu) dying list */ | ||
| 201 | ct->cpu = smp_processor_id(); | ||
| 202 | pcpu = per_cpu_ptr(nf_ct_net(ct)->ct.pcpu_lists, ct->cpu); | ||
| 203 | |||
| 204 | spin_lock(&pcpu->lock); | ||
| 205 | hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, | ||
| 206 | &pcpu->dying); | ||
| 207 | spin_unlock(&pcpu->lock); | ||
| 208 | } | ||
| 209 | |||
| 210 | /* must be called with local_bh_disable */ | ||
| 211 | static void nf_ct_add_to_unconfirmed_list(struct nf_conn *ct) | ||
| 212 | { | ||
| 213 | struct ct_pcpu *pcpu; | ||
| 214 | |||
| 215 | /* add this conntrack to the (per cpu) unconfirmed list */ | ||
| 216 | ct->cpu = smp_processor_id(); | ||
| 217 | pcpu = per_cpu_ptr(nf_ct_net(ct)->ct.pcpu_lists, ct->cpu); | ||
| 218 | |||
| 219 | spin_lock(&pcpu->lock); | ||
| 220 | hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, | ||
| 221 | &pcpu->unconfirmed); | ||
| 222 | spin_unlock(&pcpu->lock); | ||
| 223 | } | ||
| 224 | |||
| 225 | /* must be called with local_bh_disable */ | ||
| 226 | static void nf_ct_del_from_dying_or_unconfirmed_list(struct nf_conn *ct) | ||
| 227 | { | ||
| 228 | struct ct_pcpu *pcpu; | ||
| 229 | |||
| 230 | /* We overload first tuple to link into unconfirmed or dying list.*/ | ||
| 231 | pcpu = per_cpu_ptr(nf_ct_net(ct)->ct.pcpu_lists, ct->cpu); | ||
| 232 | |||
| 233 | spin_lock(&pcpu->lock); | ||
| 234 | BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode)); | ||
| 235 | hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); | ||
| 236 | spin_unlock(&pcpu->lock); | ||
| 237 | } | ||
| 238 | |||
| 195 | static void | 239 | static void |
| 196 | destroy_conntrack(struct nf_conntrack *nfct) | 240 | destroy_conntrack(struct nf_conntrack *nfct) |
| 197 | { | 241 | { |
| @@ -220,9 +264,7 @@ destroy_conntrack(struct nf_conntrack *nfct) | |||
| 220 | * too. */ | 264 | * too. */ |
| 221 | nf_ct_remove_expectations(ct); | 265 | nf_ct_remove_expectations(ct); |
| 222 | 266 | ||
| 223 | /* We overload first tuple to link into unconfirmed or dying list.*/ | 267 | nf_ct_del_from_dying_or_unconfirmed_list(ct); |
| 224 | BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode)); | ||
| 225 | hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); | ||
| 226 | 268 | ||
| 227 | NF_CT_STAT_INC(net, delete); | 269 | NF_CT_STAT_INC(net, delete); |
| 228 | spin_unlock_bh(&nf_conntrack_lock); | 270 | spin_unlock_bh(&nf_conntrack_lock); |
| @@ -244,9 +286,7 @@ static void nf_ct_delete_from_lists(struct nf_conn *ct) | |||
| 244 | * Otherwise we can get spurious warnings. */ | 286 | * Otherwise we can get spurious warnings. */ |
| 245 | NF_CT_STAT_INC(net, delete_list); | 287 | NF_CT_STAT_INC(net, delete_list); |
| 246 | clean_from_lists(ct); | 288 | clean_from_lists(ct); |
| 247 | /* add this conntrack to the dying list */ | 289 | nf_ct_add_to_dying_list(ct); |
| 248 | hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, | ||
| 249 | &net->ct.dying); | ||
| 250 | spin_unlock_bh(&nf_conntrack_lock); | 290 | spin_unlock_bh(&nf_conntrack_lock); |
| 251 | } | 291 | } |
| 252 | 292 | ||
| @@ -467,15 +507,22 @@ EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert); | |||
| 467 | /* deletion from this larval template list happens via nf_ct_put() */ | 507 | /* deletion from this larval template list happens via nf_ct_put() */ |
| 468 | void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl) | 508 | void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl) |
| 469 | { | 509 | { |
| 510 | struct ct_pcpu *pcpu; | ||
| 511 | |||
| 470 | __set_bit(IPS_TEMPLATE_BIT, &tmpl->status); | 512 | __set_bit(IPS_TEMPLATE_BIT, &tmpl->status); |
| 471 | __set_bit(IPS_CONFIRMED_BIT, &tmpl->status); | 513 | __set_bit(IPS_CONFIRMED_BIT, &tmpl->status); |
| 472 | nf_conntrack_get(&tmpl->ct_general); | 514 | nf_conntrack_get(&tmpl->ct_general); |
| 473 | 515 | ||
| 474 | spin_lock_bh(&nf_conntrack_lock); | 516 | /* add this conntrack to the (per cpu) tmpl list */ |
| 517 | local_bh_disable(); | ||
| 518 | tmpl->cpu = smp_processor_id(); | ||
| 519 | pcpu = per_cpu_ptr(nf_ct_net(tmpl)->ct.pcpu_lists, tmpl->cpu); | ||
| 520 | |||
| 521 | spin_lock(&pcpu->lock); | ||
| 475 | /* Overload tuple linked list to put us in template list. */ | 522 | /* Overload tuple linked list to put us in template list. */ |
| 476 | hlist_nulls_add_head_rcu(&tmpl->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, | 523 | hlist_nulls_add_head_rcu(&tmpl->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, |
| 477 | &net->ct.tmpl); | 524 | &pcpu->tmpl); |
| 478 | spin_unlock_bh(&nf_conntrack_lock); | 525 | spin_unlock_bh(&pcpu->lock); |
| 479 | } | 526 | } |
| 480 | EXPORT_SYMBOL_GPL(nf_conntrack_tmpl_insert); | 527 | EXPORT_SYMBOL_GPL(nf_conntrack_tmpl_insert); |
| 481 | 528 | ||
| @@ -546,8 +593,7 @@ __nf_conntrack_confirm(struct sk_buff *skb) | |||
| 546 | zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h))) | 593 | zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h))) |
| 547 | goto out; | 594 | goto out; |
| 548 | 595 | ||
| 549 | /* Remove from unconfirmed list */ | 596 | nf_ct_del_from_dying_or_unconfirmed_list(ct); |
| 550 | hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); | ||
| 551 | 597 | ||
| 552 | /* Timer relative to confirmation time, not original | 598 | /* Timer relative to confirmation time, not original |
| 553 | setting time, otherwise we'd get timer wrap in | 599 | setting time, otherwise we'd get timer wrap in |
| @@ -879,10 +925,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, | |||
| 879 | 925 | ||
| 880 | /* Now it is inserted into the unconfirmed list, bump refcount */ | 926 | /* Now it is inserted into the unconfirmed list, bump refcount */ |
| 881 | nf_conntrack_get(&ct->ct_general); | 927 | nf_conntrack_get(&ct->ct_general); |
| 882 | 928 | nf_ct_add_to_unconfirmed_list(ct); | |
| 883 | /* Overload tuple linked list to put us in unconfirmed list. */ | ||
| 884 | hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, | ||
| 885 | &net->ct.unconfirmed); | ||
| 886 | 929 | ||
| 887 | spin_unlock_bh(&nf_conntrack_lock); | 930 | spin_unlock_bh(&nf_conntrack_lock); |
| 888 | 931 | ||
| @@ -1254,6 +1297,7 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data), | |||
| 1254 | struct nf_conntrack_tuple_hash *h; | 1297 | struct nf_conntrack_tuple_hash *h; |
| 1255 | struct nf_conn *ct; | 1298 | struct nf_conn *ct; |
| 1256 | struct hlist_nulls_node *n; | 1299 | struct hlist_nulls_node *n; |
| 1300 | int cpu; | ||
| 1257 | 1301 | ||
| 1258 | spin_lock_bh(&nf_conntrack_lock); | 1302 | spin_lock_bh(&nf_conntrack_lock); |
| 1259 | for (; *bucket < net->ct.htable_size; (*bucket)++) { | 1303 | for (; *bucket < net->ct.htable_size; (*bucket)++) { |
| @@ -1265,12 +1309,19 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data), | |||
| 1265 | goto found; | 1309 | goto found; |
| 1266 | } | 1310 | } |
| 1267 | } | 1311 | } |
| 1268 | hlist_nulls_for_each_entry(h, n, &net->ct.unconfirmed, hnnode) { | ||
| 1269 | ct = nf_ct_tuplehash_to_ctrack(h); | ||
| 1270 | if (iter(ct, data)) | ||
| 1271 | set_bit(IPS_DYING_BIT, &ct->status); | ||
| 1272 | } | ||
| 1273 | spin_unlock_bh(&nf_conntrack_lock); | 1312 | spin_unlock_bh(&nf_conntrack_lock); |
| 1313 | |||
| 1314 | for_each_possible_cpu(cpu) { | ||
| 1315 | struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); | ||
| 1316 | |||
| 1317 | spin_lock_bh(&pcpu->lock); | ||
| 1318 | hlist_nulls_for_each_entry(h, n, &pcpu->unconfirmed, hnnode) { | ||
| 1319 | ct = nf_ct_tuplehash_to_ctrack(h); | ||
| 1320 | if (iter(ct, data)) | ||
| 1321 | set_bit(IPS_DYING_BIT, &ct->status); | ||
| 1322 | } | ||
| 1323 | spin_unlock_bh(&pcpu->lock); | ||
| 1324 | } | ||
| 1274 | return NULL; | 1325 | return NULL; |
| 1275 | found: | 1326 | found: |
| 1276 | atomic_inc(&ct->ct_general.use); | 1327 | atomic_inc(&ct->ct_general.use); |
| @@ -1323,14 +1374,19 @@ static void nf_ct_release_dying_list(struct net *net) | |||
| 1323 | struct nf_conntrack_tuple_hash *h; | 1374 | struct nf_conntrack_tuple_hash *h; |
| 1324 | struct nf_conn *ct; | 1375 | struct nf_conn *ct; |
| 1325 | struct hlist_nulls_node *n; | 1376 | struct hlist_nulls_node *n; |
| 1377 | int cpu; | ||
| 1326 | 1378 | ||
| 1327 | spin_lock_bh(&nf_conntrack_lock); | 1379 | for_each_possible_cpu(cpu) { |
| 1328 | hlist_nulls_for_each_entry(h, n, &net->ct.dying, hnnode) { | 1380 | struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); |
| 1329 | ct = nf_ct_tuplehash_to_ctrack(h); | 1381 | |
| 1330 | /* never fails to remove them, no listeners at this point */ | 1382 | spin_lock_bh(&pcpu->lock); |
| 1331 | nf_ct_kill(ct); | 1383 | hlist_nulls_for_each_entry(h, n, &pcpu->dying, hnnode) { |
| 1384 | ct = nf_ct_tuplehash_to_ctrack(h); | ||
| 1385 | /* never fails to remove them, no listeners at this point */ | ||
| 1386 | nf_ct_kill(ct); | ||
| 1387 | } | ||
| 1388 | spin_unlock_bh(&pcpu->lock); | ||
| 1332 | } | 1389 | } |
| 1333 | spin_unlock_bh(&nf_conntrack_lock); | ||
| 1334 | } | 1390 | } |
| 1335 | 1391 | ||
| 1336 | static int untrack_refs(void) | 1392 | static int untrack_refs(void) |
| @@ -1417,6 +1473,7 @@ i_see_dead_people: | |||
| 1417 | kmem_cache_destroy(net->ct.nf_conntrack_cachep); | 1473 | kmem_cache_destroy(net->ct.nf_conntrack_cachep); |
| 1418 | kfree(net->ct.slabname); | 1474 | kfree(net->ct.slabname); |
| 1419 | free_percpu(net->ct.stat); | 1475 | free_percpu(net->ct.stat); |
| 1476 | free_percpu(net->ct.pcpu_lists); | ||
| 1420 | } | 1477 | } |
| 1421 | } | 1478 | } |
| 1422 | 1479 | ||
| @@ -1629,37 +1686,43 @@ void nf_conntrack_init_end(void) | |||
| 1629 | 1686 | ||
| 1630 | int nf_conntrack_init_net(struct net *net) | 1687 | int nf_conntrack_init_net(struct net *net) |
| 1631 | { | 1688 | { |
| 1632 | int ret; | 1689 | int ret = -ENOMEM; |
| 1690 | int cpu; | ||
| 1633 | 1691 | ||
| 1634 | atomic_set(&net->ct.count, 0); | 1692 | atomic_set(&net->ct.count, 0); |
| 1635 | INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, UNCONFIRMED_NULLS_VAL); | 1693 | |
| 1636 | INIT_HLIST_NULLS_HEAD(&net->ct.dying, DYING_NULLS_VAL); | 1694 | net->ct.pcpu_lists = alloc_percpu(struct ct_pcpu); |
| 1637 | INIT_HLIST_NULLS_HEAD(&net->ct.tmpl, TEMPLATE_NULLS_VAL); | 1695 | if (!net->ct.pcpu_lists) |
| 1638 | net->ct.stat = alloc_percpu(struct ip_conntrack_stat); | ||
| 1639 | if (!net->ct.stat) { | ||
| 1640 | ret = -ENOMEM; | ||
| 1641 | goto err_stat; | 1696 | goto err_stat; |
| 1697 | |||
| 1698 | for_each_possible_cpu(cpu) { | ||
| 1699 | struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); | ||
| 1700 | |||
| 1701 | spin_lock_init(&pcpu->lock); | ||
| 1702 | INIT_HLIST_NULLS_HEAD(&pcpu->unconfirmed, UNCONFIRMED_NULLS_VAL); | ||
| 1703 | INIT_HLIST_NULLS_HEAD(&pcpu->dying, DYING_NULLS_VAL); | ||
| 1704 | INIT_HLIST_NULLS_HEAD(&pcpu->tmpl, TEMPLATE_NULLS_VAL); | ||
| 1642 | } | 1705 | } |
| 1643 | 1706 | ||
| 1707 | net->ct.stat = alloc_percpu(struct ip_conntrack_stat); | ||
| 1708 | if (!net->ct.stat) | ||
| 1709 | goto err_pcpu_lists; | ||
| 1710 | |||
| 1644 | net->ct.slabname = kasprintf(GFP_KERNEL, "nf_conntrack_%p", net); | 1711 | net->ct.slabname = kasprintf(GFP_KERNEL, "nf_conntrack_%p", net); |
| 1645 | if (!net->ct.slabname) { | 1712 | if (!net->ct.slabname) |
| 1646 | ret = -ENOMEM; | ||
| 1647 | goto err_slabname; | 1713 | goto err_slabname; |
| 1648 | } | ||
| 1649 | 1714 | ||
| 1650 | net->ct.nf_conntrack_cachep = kmem_cache_create(net->ct.slabname, | 1715 | net->ct.nf_conntrack_cachep = kmem_cache_create(net->ct.slabname, |
| 1651 | sizeof(struct nf_conn), 0, | 1716 | sizeof(struct nf_conn), 0, |
| 1652 | SLAB_DESTROY_BY_RCU, NULL); | 1717 | SLAB_DESTROY_BY_RCU, NULL); |
| 1653 | if (!net->ct.nf_conntrack_cachep) { | 1718 | if (!net->ct.nf_conntrack_cachep) { |
| 1654 | printk(KERN_ERR "Unable to create nf_conn slab cache\n"); | 1719 | printk(KERN_ERR "Unable to create nf_conn slab cache\n"); |
| 1655 | ret = -ENOMEM; | ||
| 1656 | goto err_cache; | 1720 | goto err_cache; |
| 1657 | } | 1721 | } |
| 1658 | 1722 | ||
| 1659 | net->ct.htable_size = nf_conntrack_htable_size; | 1723 | net->ct.htable_size = nf_conntrack_htable_size; |
| 1660 | net->ct.hash = nf_ct_alloc_hashtable(&net->ct.htable_size, 1); | 1724 | net->ct.hash = nf_ct_alloc_hashtable(&net->ct.htable_size, 1); |
| 1661 | if (!net->ct.hash) { | 1725 | if (!net->ct.hash) { |
| 1662 | ret = -ENOMEM; | ||
| 1663 | printk(KERN_ERR "Unable to create nf_conntrack_hash\n"); | 1726 | printk(KERN_ERR "Unable to create nf_conntrack_hash\n"); |
| 1664 | goto err_hash; | 1727 | goto err_hash; |
| 1665 | } | 1728 | } |
| @@ -1701,6 +1764,8 @@ err_cache: | |||
| 1701 | kfree(net->ct.slabname); | 1764 | kfree(net->ct.slabname); |
| 1702 | err_slabname: | 1765 | err_slabname: |
| 1703 | free_percpu(net->ct.stat); | 1766 | free_percpu(net->ct.stat); |
| 1767 | err_pcpu_lists: | ||
| 1768 | free_percpu(net->ct.pcpu_lists); | ||
| 1704 | err_stat: | 1769 | err_stat: |
| 1705 | return ret; | 1770 | return ret; |
| 1706 | } | 1771 | } |
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 974a2a4adefa..27d9302c2191 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c | |||
| @@ -396,6 +396,7 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, | |||
| 396 | const struct hlist_node *next; | 396 | const struct hlist_node *next; |
| 397 | const struct hlist_nulls_node *nn; | 397 | const struct hlist_nulls_node *nn; |
| 398 | unsigned int i; | 398 | unsigned int i; |
| 399 | int cpu; | ||
| 399 | 400 | ||
| 400 | /* Get rid of expectations */ | 401 | /* Get rid of expectations */ |
| 401 | for (i = 0; i < nf_ct_expect_hsize; i++) { | 402 | for (i = 0; i < nf_ct_expect_hsize; i++) { |
| @@ -414,8 +415,14 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, | |||
| 414 | } | 415 | } |
| 415 | 416 | ||
| 416 | /* Get rid of expecteds, set helpers to NULL. */ | 417 | /* Get rid of expecteds, set helpers to NULL. */ |
| 417 | hlist_nulls_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode) | 418 | for_each_possible_cpu(cpu) { |
| 418 | unhelp(h, me); | 419 | struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); |
| 420 | |||
| 421 | spin_lock_bh(&pcpu->lock); | ||
| 422 | hlist_nulls_for_each_entry(h, nn, &pcpu->unconfirmed, hnnode) | ||
| 423 | unhelp(h, me); | ||
| 424 | spin_unlock_bh(&pcpu->lock); | ||
| 425 | } | ||
| 419 | for (i = 0; i < net->ct.htable_size; i++) { | 426 | for (i = 0; i < net->ct.htable_size; i++) { |
| 420 | hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode) | 427 | hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode) |
| 421 | unhelp(h, me); | 428 | unhelp(h, me); |
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 47e9369997ef..4ac8ce68bc16 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c | |||
| @@ -1137,50 +1137,65 @@ static int ctnetlink_done_list(struct netlink_callback *cb) | |||
| 1137 | } | 1137 | } |
| 1138 | 1138 | ||
| 1139 | static int | 1139 | static int |
| 1140 | ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb, | 1140 | ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb, bool dying) |
| 1141 | struct hlist_nulls_head *list) | ||
| 1142 | { | 1141 | { |
| 1143 | struct nf_conn *ct, *last; | 1142 | struct nf_conn *ct, *last = NULL; |
| 1144 | struct nf_conntrack_tuple_hash *h; | 1143 | struct nf_conntrack_tuple_hash *h; |
| 1145 | struct hlist_nulls_node *n; | 1144 | struct hlist_nulls_node *n; |
| 1146 | struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); | 1145 | struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); |
| 1147 | u_int8_t l3proto = nfmsg->nfgen_family; | 1146 | u_int8_t l3proto = nfmsg->nfgen_family; |
| 1148 | int res; | 1147 | int res; |
| 1148 | int cpu; | ||
| 1149 | struct hlist_nulls_head *list; | ||
| 1150 | struct net *net = sock_net(skb->sk); | ||
| 1149 | 1151 | ||
| 1150 | if (cb->args[2]) | 1152 | if (cb->args[2]) |
| 1151 | return 0; | 1153 | return 0; |
| 1152 | 1154 | ||
| 1153 | spin_lock_bh(&nf_conntrack_lock); | 1155 | if (cb->args[0] == nr_cpu_ids) |
| 1154 | last = (struct nf_conn *)cb->args[1]; | 1156 | return 0; |
| 1155 | restart: | 1157 | |
| 1156 | hlist_nulls_for_each_entry(h, n, list, hnnode) { | 1158 | for (cpu = cb->args[0]; cpu < nr_cpu_ids; cpu++) { |
| 1157 | ct = nf_ct_tuplehash_to_ctrack(h); | 1159 | struct ct_pcpu *pcpu; |
| 1158 | if (l3proto && nf_ct_l3num(ct) != l3proto) | 1160 | |
| 1161 | if (!cpu_possible(cpu)) | ||
| 1159 | continue; | 1162 | continue; |
| 1160 | if (cb->args[1]) { | 1163 | |
| 1161 | if (ct != last) | 1164 | pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); |
| 1165 | spin_lock_bh(&pcpu->lock); | ||
| 1166 | last = (struct nf_conn *)cb->args[1]; | ||
| 1167 | list = dying ? &pcpu->dying : &pcpu->unconfirmed; | ||
| 1168 | restart: | ||
| 1169 | hlist_nulls_for_each_entry(h, n, list, hnnode) { | ||
| 1170 | ct = nf_ct_tuplehash_to_ctrack(h); | ||
| 1171 | if (l3proto && nf_ct_l3num(ct) != l3proto) | ||
| 1162 | continue; | 1172 | continue; |
| 1163 | cb->args[1] = 0; | 1173 | if (cb->args[1]) { |
| 1164 | } | 1174 | if (ct != last) |
| 1165 | rcu_read_lock(); | 1175 | continue; |
| 1166 | res = ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid, | 1176 | cb->args[1] = 0; |
| 1167 | cb->nlh->nlmsg_seq, | 1177 | } |
| 1168 | NFNL_MSG_TYPE(cb->nlh->nlmsg_type), | 1178 | rcu_read_lock(); |
| 1169 | ct); | 1179 | res = ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid, |
| 1170 | rcu_read_unlock(); | 1180 | cb->nlh->nlmsg_seq, |
| 1171 | if (res < 0) { | 1181 | NFNL_MSG_TYPE(cb->nlh->nlmsg_type), |
| 1172 | nf_conntrack_get(&ct->ct_general); | 1182 | ct); |
| 1173 | cb->args[1] = (unsigned long)ct; | 1183 | rcu_read_unlock(); |
| 1174 | goto out; | 1184 | if (res < 0) { |
| 1185 | nf_conntrack_get(&ct->ct_general); | ||
| 1186 | cb->args[1] = (unsigned long)ct; | ||
| 1187 | spin_unlock_bh(&pcpu->lock); | ||
| 1188 | goto out; | ||
| 1189 | } | ||
| 1175 | } | 1190 | } |
| 1191 | if (cb->args[1]) { | ||
| 1192 | cb->args[1] = 0; | ||
| 1193 | goto restart; | ||
| 1194 | } else | ||
| 1195 | cb->args[2] = 1; | ||
| 1196 | spin_unlock_bh(&pcpu->lock); | ||
| 1176 | } | 1197 | } |
| 1177 | if (cb->args[1]) { | ||
| 1178 | cb->args[1] = 0; | ||
| 1179 | goto restart; | ||
| 1180 | } else | ||
| 1181 | cb->args[2] = 1; | ||
| 1182 | out: | 1198 | out: |
| 1183 | spin_unlock_bh(&nf_conntrack_lock); | ||
| 1184 | if (last) | 1199 | if (last) |
| 1185 | nf_ct_put(last); | 1200 | nf_ct_put(last); |
| 1186 | 1201 | ||
| @@ -1190,9 +1205,7 @@ out: | |||
| 1190 | static int | 1205 | static int |
| 1191 | ctnetlink_dump_dying(struct sk_buff *skb, struct netlink_callback *cb) | 1206 | ctnetlink_dump_dying(struct sk_buff *skb, struct netlink_callback *cb) |
| 1192 | { | 1207 | { |
| 1193 | struct net *net = sock_net(skb->sk); | 1208 | return ctnetlink_dump_list(skb, cb, true); |
| 1194 | |||
| 1195 | return ctnetlink_dump_list(skb, cb, &net->ct.dying); | ||
| 1196 | } | 1209 | } |
| 1197 | 1210 | ||
| 1198 | static int | 1211 | static int |
| @@ -1214,9 +1227,7 @@ ctnetlink_get_ct_dying(struct sock *ctnl, struct sk_buff *skb, | |||
| 1214 | static int | 1227 | static int |
| 1215 | ctnetlink_dump_unconfirmed(struct sk_buff *skb, struct netlink_callback *cb) | 1228 | ctnetlink_dump_unconfirmed(struct sk_buff *skb, struct netlink_callback *cb) |
| 1216 | { | 1229 | { |
| 1217 | struct net *net = sock_net(skb->sk); | 1230 | return ctnetlink_dump_list(skb, cb, false); |
| 1218 | |||
| 1219 | return ctnetlink_dump_list(skb, cb, &net->ct.unconfirmed); | ||
| 1220 | } | 1231 | } |
| 1221 | 1232 | ||
| 1222 | static int | 1233 | static int |
