summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2014-11-04 20:48:21 +0100
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2014-11-04 20:48:21 +0100
commitb903fea8b6241fc9bf761d25c08979c999f0d5a9 (patch)
tree48c571df4190b563473a2be5f89b9c861acd4b72
parent30f4a463c0255441a9c406dc6a3056f3343ff3c6 (diff)
The new method for parallel listing and resizing was broken, fixed.rcu
-rw-r--r--kernel/net/netfilter/ipset/ip_set_core.c18
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_gen.h16
2 files changed, 18 insertions, 16 deletions
diff --git a/kernel/net/netfilter/ipset/ip_set_core.c b/kernel/net/netfilter/ipset/ip_set_core.c
index ee3e09c..b20c209 100644
--- a/kernel/net/netfilter/ipset/ip_set_core.c
+++ b/kernel/net/netfilter/ipset/ip_set_core.c
@@ -1194,7 +1194,7 @@ ip_set_dump_done(struct netlink_callback *cb)
ip_set_id_t index = (ip_set_id_t) cb->args[IPSET_CB_INDEX];
struct ip_set *set = ip_set(inst, index);
- if (set->variant->uref && cb->args[IPSET_CB_PRIVATE])
+ if (set->variant->uref)
set->variant->uref(set, cb, false);
pr_debug("release set %s\n", set->name);
__ip_set_put_byindex(inst, index);
@@ -1262,7 +1262,6 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
struct ip_set_net *inst = ip_set_pernet(sock_net(skb->sk));
u32 dump_type, dump_flags;
int ret = 0;
- bool uref = false;
if (!cb->args[IPSET_CB_DUMP]) {
ret = dump_init(cb, inst);
@@ -1336,10 +1335,8 @@ dump_last:
goto release_refcount;
if (dump_flags & IPSET_FLAG_LIST_HEADER)
goto next_set;
- if (set->variant->uref) {
- uref = true;
+ if (set->variant->uref)
set->variant->uref(set, cb, true);
- }
/* Fall through and add elements */
default:
rcu_read_lock_bh();
@@ -1356,10 +1353,8 @@ dump_last:
dump_type = DUMP_LAST;
cb->args[IPSET_CB_DUMP] = dump_type | (dump_flags << 16);
cb->args[IPSET_CB_INDEX] = 0;
- if (uref) {
- uref = false;
+ if (set && set->variant->uref)
set->variant->uref(set, cb, false);
- }
goto dump_last;
}
goto out;
@@ -1374,7 +1369,10 @@ next_set:
release_refcount:
/* If there was an error or set is done, release set */
if (ret || !cb->args[IPSET_CB_ARG0]) {
- pr_debug("release set %s\n", ip_set(inst, index)->name);
+ set = ip_set(inst, index);
+ if (set->variant->uref)
+ set->variant->uref(set, cb, false);
+ pr_debug("release set %s\n", set->name);
__ip_set_put_byindex(inst, index);
cb->args[IPSET_CB_ARG0] = 0;
}
@@ -1384,8 +1382,6 @@ out:
pr_debug("nlmsg_len: %u\n", nlh->nlmsg_len);
dump_attrs(nlh);
}
- if (uref)
- set->variant->uref(set, cb, false);
return ret < 0 ? ret : skb->len;
}
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_gen.h b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
index 2ffd2da..6c8fc0b 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
@@ -566,7 +566,8 @@ retry:
spin_lock_bh(&set->lock);
orig = h->table;
- atomic_inc(&orig->ref);
+ atomic_set(&orig->ref, 1);
+ atomic_inc(&orig->uref);
pr_debug("attempt to resize set %s from %u to %u, t %p\n",
set->name, orig->htable_bits, htable_bits, orig);
for (i = 0; i < jhash_size(orig->htable_bits); i++) {
@@ -610,9 +611,10 @@ retry:
ret = -ENOMEM;
}
if (ret < 0) {
+ atomic_set(&orig->ref, 0);
+ atomic_dec(&orig->uref);
spin_unlock_bh(&set->lock);
mtype_ahash_destroy(set, t, false);
- atomic_dec(&orig->ref);
if (ret == -EAGAIN)
goto retry;
return ret;
@@ -641,8 +643,10 @@ retry:
pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name,
orig->htable_bits, orig, t->htable_bits, t);
/* If there's nobody else dumping the table, destroy it */
- if (atomic_read(&orig->uref) == 0)
+ if (atomic_dec_and_test(&orig->uref)) {
+ pr_debug("Table destroy by resize %p\n", orig);
mtype_ahash_destroy(set, orig, false);
+ }
return 0;
@@ -1053,11 +1057,13 @@ mtype_uref(struct ip_set *set, struct netlink_callback *cb, bool start)
atomic_inc(&t->uref);
cb->args[IPSET_CB_PRIVATE] = (unsigned long) t;
rcu_read_unlock_bh();
- } else {
+ } else if (cb->args[IPSET_CB_PRIVATE]) {
t = (struct htable *) cb->args[IPSET_CB_PRIVATE];
- if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref))
+ if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
/* Resizing didn't destroy the hash table */
+ pr_debug("Table destroy by dump: %p\n", t);
mtype_ahash_destroy(set, t, false);
+ }
cb->args[IPSET_CB_PRIVATE] = 0;
}
}