diff options
author | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2014-11-04 20:48:21 +0100 |
---|---|---|
committer | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2014-11-04 20:48:21 +0100 |
commit | b903fea8b6241fc9bf761d25c08979c999f0d5a9 (patch) | |
tree | 48c571df4190b563473a2be5f89b9c861acd4b72 | |
parent | 30f4a463c0255441a9c406dc6a3056f3343ff3c6 (diff) | |
download | ipset-rcu.tar.gz |
The new method for parallel listing and resizing was broken, fixed.rcu
-rw-r--r-- | kernel/net/netfilter/ipset/ip_set_core.c | 18 | ||||
-rw-r--r-- | kernel/net/netfilter/ipset/ip_set_hash_gen.h | 16 |
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; } } |