aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/nf_conncount.c46
1 files changed, 20 insertions, 26 deletions
diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c
index ce7f7d1212a6..d0fd195b19a8 100644
--- a/net/netfilter/nf_conncount.c
+++ b/net/netfilter/nf_conncount.c
@@ -43,8 +43,6 @@ struct nf_conncount_tuple {
43 struct nf_conntrack_zone zone; 43 struct nf_conntrack_zone zone;
44 int cpu; 44 int cpu;
45 u32 jiffies32; 45 u32 jiffies32;
46 bool dead;
47 struct rcu_head rcu_head;
48}; 46};
49 47
50struct nf_conncount_rb { 48struct nf_conncount_rb {
@@ -83,36 +81,21 @@ static int key_diff(const u32 *a, const u32 *b, unsigned int klen)
83 return memcmp(a, b, klen * sizeof(u32)); 81 return memcmp(a, b, klen * sizeof(u32));
84} 82}
85 83
86static void __conn_free(struct rcu_head *h)
87{
88 struct nf_conncount_tuple *conn;
89
90 conn = container_of(h, struct nf_conncount_tuple, rcu_head);
91 kmem_cache_free(conncount_conn_cachep, conn);
92}
93
94static bool conn_free(struct nf_conncount_list *list, 84static bool conn_free(struct nf_conncount_list *list,
95 struct nf_conncount_tuple *conn) 85 struct nf_conncount_tuple *conn)
96{ 86{
97 bool free_entry = false; 87 bool free_entry = false;
98 88
99 spin_lock_bh(&list->list_lock); 89 lockdep_assert_held(&list->list_lock);
100
101 if (conn->dead) {
102 spin_unlock_bh(&list->list_lock);
103 return free_entry;
104 }
105 90
106 list->count--; 91 list->count--;
107 conn->dead = true; 92 list_del(&conn->node);
108 list_del_rcu(&conn->node);
109 if (list->count == 0) { 93 if (list->count == 0) {
110 list->dead = true; 94 list->dead = true;
111 free_entry = true; 95 free_entry = true;
112 } 96 }
113 97
114 spin_unlock_bh(&list->list_lock); 98 kmem_cache_free(conncount_conn_cachep, conn);
115 call_rcu(&conn->rcu_head, __conn_free);
116 return free_entry; 99 return free_entry;
117} 100}
118 101
@@ -242,7 +225,7 @@ void nf_conncount_list_init(struct nf_conncount_list *list)
242} 225}
243EXPORT_SYMBOL_GPL(nf_conncount_list_init); 226EXPORT_SYMBOL_GPL(nf_conncount_list_init);
244 227
245/* Return true if the list is empty */ 228/* Return true if the list is empty. Must be called with BH disabled. */
246bool nf_conncount_gc_list(struct net *net, 229bool nf_conncount_gc_list(struct net *net,
247 struct nf_conncount_list *list) 230 struct nf_conncount_list *list)
248{ 231{
@@ -253,12 +236,18 @@ bool nf_conncount_gc_list(struct net *net,
253 bool free_entry = false; 236 bool free_entry = false;
254 bool ret = false; 237 bool ret = false;
255 238
239 /* don't bother if other cpu is already doing GC */
240 if (!spin_trylock(&list->list_lock))
241 return false;
242
256 list_for_each_entry_safe(conn, conn_n, &list->head, node) { 243 list_for_each_entry_safe(conn, conn_n, &list->head, node) {
257 found = find_or_evict(net, list, conn, &free_entry); 244 found = find_or_evict(net, list, conn, &free_entry);
258 if (IS_ERR(found)) { 245 if (IS_ERR(found)) {
259 if (PTR_ERR(found) == -ENOENT) { 246 if (PTR_ERR(found) == -ENOENT) {
260 if (free_entry) 247 if (free_entry) {
248 spin_unlock(&list->list_lock);
261 return true; 249 return true;
250 }
262 collected++; 251 collected++;
263 } 252 }
264 continue; 253 continue;
@@ -271,23 +260,24 @@ bool nf_conncount_gc_list(struct net *net,
271 * closed already -> ditch it 260 * closed already -> ditch it
272 */ 261 */
273 nf_ct_put(found_ct); 262 nf_ct_put(found_ct);
274 if (conn_free(list, conn)) 263 if (conn_free(list, conn)) {
264 spin_unlock(&list->list_lock);
275 return true; 265 return true;
266 }
276 collected++; 267 collected++;
277 continue; 268 continue;
278 } 269 }
279 270
280 nf_ct_put(found_ct); 271 nf_ct_put(found_ct);
281 if (collected > CONNCOUNT_GC_MAX_NODES) 272 if (collected > CONNCOUNT_GC_MAX_NODES)
282 return false; 273 break;
283 } 274 }
284 275
285 spin_lock_bh(&list->list_lock);
286 if (!list->count) { 276 if (!list->count) {
287 list->dead = true; 277 list->dead = true;
288 ret = true; 278 ret = true;
289 } 279 }
290 spin_unlock_bh(&list->list_lock); 280 spin_unlock(&list->list_lock);
291 281
292 return ret; 282 return ret;
293} 283}
@@ -478,6 +468,7 @@ static void tree_gc_worker(struct work_struct *work)
478 tree = data->gc_tree % CONNCOUNT_SLOTS; 468 tree = data->gc_tree % CONNCOUNT_SLOTS;
479 root = &data->root[tree]; 469 root = &data->root[tree];
480 470
471 local_bh_disable();
481 rcu_read_lock(); 472 rcu_read_lock();
482 for (node = rb_first(root); node != NULL; node = rb_next(node)) { 473 for (node = rb_first(root); node != NULL; node = rb_next(node)) {
483 rbconn = rb_entry(node, struct nf_conncount_rb, node); 474 rbconn = rb_entry(node, struct nf_conncount_rb, node);
@@ -485,6 +476,9 @@ static void tree_gc_worker(struct work_struct *work)
485 gc_count++; 476 gc_count++;
486 } 477 }
487 rcu_read_unlock(); 478 rcu_read_unlock();
479 local_bh_enable();
480
481 cond_resched();
488 482
489 spin_lock_bh(&nf_conncount_locks[tree]); 483 spin_lock_bh(&nf_conncount_locks[tree]);
490 if (gc_count < ARRAY_SIZE(gc_nodes)) 484 if (gc_count < ARRAY_SIZE(gc_nodes))