struct nf_conncount_root {
struct rb_root root;
spinlock_t lock;
+ seqcount_spinlock_t count;
};
struct nf_conncount_data {
rbconn = gc_nodes[--gc_count];
spin_lock(&rbconn->list.list_lock);
if (!rbconn->list.count) {
+ write_seqcount_begin(&root->count);
rb_erase(&rbconn->node, &root->root);
call_rcu(&rbconn->rcu_head, __tree_nodes_free);
+ write_seqcount_end(&root->count);
}
spin_unlock(&rbconn->list.list_lock);
}
count = 1;
rbconn->list.count = count;
+ write_seqcount_begin(&root->count);
rb_link_node_rcu(&rbconn->node, parent, rbnode);
rb_insert_color(&rbconn->node, &root->root);
+ write_seqcount_end(&root->count);
}
out_unlock:
if (refcounted)
find_tree_node(struct nf_conncount_root *root, struct nf_conncount_data *data,
const u32 *key)
{
+ unsigned int seq = read_seqcount_begin(&root->count);
struct rb_node *parent;
parent = rcu_dereference_check(root->root.rb_node,
lockdep_is_held(&root->lock));
else
return rbconn;
+
+ if (read_seqcount_retry(&root->count, seq))
+ return ERR_PTR(-EAGAIN);
}
+ if (read_seqcount_retry(&root->count, seq))
+ return ERR_PTR(-EAGAIN);
+
return ERR_PTR(-ENOENT);
}
rbconn = find_tree_node(root, data, key);
if (IS_ERR(rbconn)) {
+ if (PTR_ERR(rbconn) == -EAGAIN) {
+ spin_lock_bh(&root->lock);
+ rbconn = find_tree_node(root, data, key);
+ spin_unlock_bh(&root->lock);
+ }
+
if (PTR_ERR(rbconn) == -ENOENT) {
if (!skb)
return 0;
{
r->root = RB_ROOT;
spin_lock_init(&r->lock);
+ seqcount_spinlock_init(&r->count, &r->lock);
}
struct nf_conncount_data *nf_conncount_init(struct net *net, unsigned int keylen)