{
struct nf_conncount_data *data = container_of(work, struct nf_conncount_data, gc_work);
struct nf_conncount_rb *gc_nodes[CONNCOUNT_GC_MAX_NODES], *rbconn;
+ unsigned int tree, next_tree, gc_count = 0;
struct nf_conncount_root *root;
struct rb_node *node;
- unsigned int tree, next_tree, gc_count = 0;
+
+ if (data->gc_tree == 0)
+ data->gc_tree = find_first_bit(data->pending_trees, CONNCOUNT_SLOTS);
tree = data->gc_tree % CONNCOUNT_SLOTS;
root = &data->root[tree];
- local_bh_disable();
- rcu_read_lock();
- for (node = rb_first(&root->root); node ; node = rb_next(node)) {
- rbconn = rb_entry(node, struct nf_conncount_rb, node);
- if (nf_conncount_gc_list(data->net, &rbconn->list))
- gc_count++;
- }
- rcu_read_unlock();
- local_bh_enable();
-
- cond_resched();
-
spin_lock_bh(&root->lock);
- if (gc_count < ARRAY_SIZE(gc_nodes))
- goto next; /* do not bother */
-
gc_count = 0;
node = rb_first(&root->root);
while (node != NULL) {
+ u32 key[MAX_KEYLEN];
+ bool drop_lock;
+
rbconn = rb_entry(node, struct nf_conncount_rb, node);
node = rb_next(node);
- if (rbconn->list.count > 0)
- continue;
+ if (nf_conncount_gc_list(data->net, &rbconn->list))
+ gc_nodes[gc_count++] = rbconn;
+
+ drop_lock = need_resched();
- gc_nodes[gc_count++] = rbconn;
- if (gc_count >= ARRAY_SIZE(gc_nodes)) {
+ if (drop_lock || gc_count >= ARRAY_SIZE(gc_nodes)) {
tree_nodes_free(root, gc_nodes, gc_count);
gc_count = 0;
}
+
+ if (!drop_lock || !node)
+ continue;
+
+ rbconn = rb_entry(node, struct nf_conncount_rb, node);
+ memcpy(key, rbconn->key, sizeof(key));
+ spin_unlock_bh(&root->lock);
+
+ cond_resched();
+
+ spin_lock_bh(&root->lock);
+ rbconn = find_tree_node(root, data, key);
+ if (IS_ERR_OR_NULL(rbconn)) /* rbconn was reaped */
+ break;
+
+ node = &rbconn->node;
}
tree_nodes_free(root, gc_nodes, gc_count);
-next:
clear_bit(tree, data->pending_trees);
next_tree = (tree + 1) % CONNCOUNT_SLOTS;
if (next_tree < CONNCOUNT_SLOTS) {
data->gc_tree = next_tree;
schedule_work(work);
+ } else {
+ data->gc_tree = 0;
}
spin_unlock_bh(&root->lock);
{
unsigned int i;
- cancel_work_sync(&data->gc_work);
+ disable_work_sync(&data->gc_work);
for (i = 0; i < ARRAY_SIZE(data->root); ++i)
destroy_tree(&data->root[i]);
static void __exit nf_conncount_modexit(void)
{
+ rcu_barrier();
kmem_cache_destroy(conncount_conn_cachep);
kmem_cache_destroy(conncount_rb_cachep);
}