summaryrefslogtreecommitdiffstats
path: root/kernel/net/netfilter/ipset/ip_set_core.c
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@netfilter.org>2024-02-05 11:30:51 +0100
committerJozsef Kadlecsik <kadlec@netfilter.org>2024-02-05 12:30:51 +0100
commitb2f78c1935480208e9629a73e7967e5842282e7a (patch)
treee14eae32fbfda5ffc7ccae6354b3ac4032d1622a /kernel/net/netfilter/ipset/ip_set_core.c
parent917671418de146c23470d29d94d25104a5c9ca06 (diff)
netfilter: ipset: Cleanup the code of destroy operation and explain the two stages in comments
Signed-off-by: Jozsef Kadlecsik <kadlec@netfilter.org>
Diffstat (limited to 'kernel/net/netfilter/ipset/ip_set_core.c')
-rw-r--r--kernel/net/netfilter/ipset/ip_set_core.c44
1 files changed, 33 insertions, 11 deletions
diff --git a/kernel/net/netfilter/ipset/ip_set_core.c b/kernel/net/netfilter/ipset/ip_set_core.c
index 94cc913..a99c1bd 100644
--- a/kernel/net/netfilter/ipset/ip_set_core.c
+++ b/kernel/net/netfilter/ipset/ip_set_core.c
@@ -1182,23 +1182,48 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = {
.len = IPSET_MAXNAMELEN - 1 },
};
+/* Destroying a set is split into two stages when a DESTROY command issued:
+ * - Cancel garbage collectors and decrement the module reference counter:
+ * - Cancelling may wait and we are allowed to do it at this stage.
+ * - Module remove is protected by rcu_barrier() which waits for
+ * the second stage to be finished.
+ * - In order to prevent the race between kernel side add/del/test element
+ * operations and destroy, the destroying of the set data areas are
+ * performed via a call_rcu() call.
+ */
+
+/* Call set variant specific destroy function and reclaim the set data. */
static void
-ip_set_destroy_set(struct ip_set *set)
+ip_set_destroy_set_variant(struct ip_set *set)
{
- pr_debug("set: %s\n", set->name);
-
/* Must call it without holding any lock */
set->variant->destroy(set);
- module_put(set->type->me);
kfree(set);
}
static void
-ip_set_destroy_set_rcu(struct rcu_head *head)
+ip_set_destroy_set_variant_rcu(struct rcu_head *head)
{
struct ip_set *set = container_of(head, struct ip_set, rcu);
- ip_set_destroy_set(set);
+ ip_set_destroy_set_variant(set);
+}
+
+/* Cancel the garbage collectors and decrement module references */
+static void
+ip_set_destroy_cancel_gc(struct ip_set *set)
+{
+ set->variant->cancel_gc(set);
+ module_put(set->type->me);
+}
+
+/* Use when we may wait for the complete destroy to be finished.
+ */
+static void
+ip_set_destroy_set(struct ip_set *set)
+{
+ ip_set_destroy_cancel_gc(set);
+ ip_set_destroy_set_variant(set);
}
static int
@@ -1243,8 +1268,6 @@ IPSET_CBFN(ip_set_destroy, struct net *net, struct sock *ctnl,
s = ip_set(inst, i);
if (s) {
ip_set(inst, i) = NULL;
- /* Must cancel garbage collectors */
- s->variant->cancel_gc(s);
ip_set_destroy_set(s);
}
}
@@ -1273,8 +1296,8 @@ IPSET_CBFN(ip_set_destroy, struct net *net, struct sock *ctnl,
rcu_barrier();
}
/* Must cancel garbage collectors */
- s->variant->cancel_gc(s);
- call_rcu(&s->rcu, ip_set_destroy_set_rcu);
+ ip_set_destroy_cancel_gc(s);
+ call_rcu(&s->rcu, ip_set_destroy_set_variant_rcu);
}
return 0;
out:
@@ -2488,7 +2511,6 @@ ip_set_net_exit(struct net *net)
set = ip_set(inst, i);
if (set) {
ip_set(inst, i) = NULL;
- set->variant->cancel_gc(set);
ip_set_destroy_set(set);
}
}