Blame SOURCES/0018-nft-cache-Sort-chains-on-demand-only.patch

3a00e5
From 743bcc5a632c7f5058ac03794f82b7ba52091cea Mon Sep 17 00:00:00 2001
3a00e5
From: Phil Sutter <phil@nwl.cc>
3a00e5
Date: Thu, 25 Mar 2021 16:24:39 +0100
3a00e5
Subject: [PATCH] nft: cache: Sort chains on demand only
3a00e5
3a00e5
Mandatory sorted insert of chains into cache significantly slows down
3a00e5
restoring of large rulesets. Since the sorted list of user-defined
3a00e5
chains is needed for listing and verbose output only, introduce
3a00e5
nft_cache_sort_chains() and call it where needed.
3a00e5
3a00e5
Signed-off-by: Phil Sutter <phil@nwl.cc>
3a00e5
(cherry picked from commit fdf64dcdace989589bac441805082e3b1fe6a915)
3a00e5
---
3a00e5
 iptables/nft-cache.c    | 71 +++++++++++++++++++++++++++++++++--------
3a00e5
 iptables/nft-cache.h    |  1 +
3a00e5
 iptables/nft.c          | 12 +++++++
3a00e5
 iptables/nft.h          |  1 +
3a00e5
 iptables/xtables-save.c |  1 +
3a00e5
 5 files changed, 73 insertions(+), 13 deletions(-)
3a00e5
3a00e5
diff --git a/iptables/nft-cache.c b/iptables/nft-cache.c
3a00e5
index 7fd78654b280a..2c88301cc7445 100644
3a00e5
--- a/iptables/nft-cache.c
3a00e5
+++ b/iptables/nft-cache.c
3a00e5
@@ -223,24 +223,67 @@ int nft_cache_add_chain(struct nft_handle *h, const struct builtin_table *t,
3a00e5
 
3a00e5
 		h->cache->table[t->type].base_chains[hooknum] = nc;
3a00e5
 	} else {
3a00e5
-		struct nft_chain_list *clist = h->cache->table[t->type].chains;
3a00e5
-		struct list_head *pos = &clist->list;
3a00e5
-		struct nft_chain *cur;
3a00e5
-		const char *n;
3a00e5
-
3a00e5
-		list_for_each_entry(cur, &clist->list, head) {
3a00e5
-			n = nftnl_chain_get_str(cur->nftnl, NFTNL_CHAIN_NAME);
3a00e5
-			if (strcmp(cname, n) <= 0) {
3a00e5
-				pos = &cur->head;
3a00e5
-				break;
3a00e5
-			}
3a00e5
-		}
3a00e5
-		list_add_tail(&nc->head, pos);
3a00e5
+		list_add_tail(&nc->head,
3a00e5
+			      &h->cache->table[t->type].chains->list);
3a00e5
 	}
3a00e5
 	hlist_add_head(&nc->hnode, chain_name_hlist(h, t, cname));
3a00e5
 	return 0;
3a00e5
 }
3a00e5
 
3a00e5
+static void __nft_chain_list_sort(struct list_head *list,
3a00e5
+				  int (*cmp)(struct nft_chain *a,
3a00e5
+					     struct nft_chain *b))
3a00e5
+{
3a00e5
+	struct nft_chain *pivot, *cur, *sav;
3a00e5
+	LIST_HEAD(sublist);
3a00e5
+
3a00e5
+	if (list_empty(list))
3a00e5
+		return;
3a00e5
+
3a00e5
+	/* grab first item as pivot (dividing) value */
3a00e5
+	pivot = list_entry(list->next, struct nft_chain, head);
3a00e5
+	list_del(&pivot->head);
3a00e5
+
3a00e5
+	/* move any smaller value into sublist */
3a00e5
+	list_for_each_entry_safe(cur, sav, list, head) {
3a00e5
+		if (cmp(pivot, cur) > 0) {
3a00e5
+			list_del(&cur->head);
3a00e5
+			list_add_tail(&cur->head, &sublist);
3a00e5
+		}
3a00e5
+	}
3a00e5
+	/* conquer divided */
3a00e5
+	__nft_chain_list_sort(&sublist, cmp);
3a00e5
+	__nft_chain_list_sort(list, cmp);
3a00e5
+
3a00e5
+	/* merge divided and pivot again */
3a00e5
+	list_add_tail(&pivot->head, &sublist);
3a00e5
+	list_splice(&sublist, list);
3a00e5
+}
3a00e5
+
3a00e5
+static int nft_chain_cmp_byname(struct nft_chain *a, struct nft_chain *b)
3a00e5
+{
3a00e5
+	const char *aname = nftnl_chain_get_str(a->nftnl, NFTNL_CHAIN_NAME);
3a00e5
+	const char *bname = nftnl_chain_get_str(b->nftnl, NFTNL_CHAIN_NAME);
3a00e5
+
3a00e5
+	return strcmp(aname, bname);
3a00e5
+}
3a00e5
+
3a00e5
+int nft_cache_sort_chains(struct nft_handle *h, const char *table)
3a00e5
+{
3a00e5
+	const struct builtin_table *t = nft_table_builtin_find(h, table);
3a00e5
+
3a00e5
+	if (!t)
3a00e5
+		return -1;
3a00e5
+
3a00e5
+	if (h->cache->table[t->type].sorted)
3a00e5
+		return 0;
3a00e5
+
3a00e5
+	__nft_chain_list_sort(&h->cache->table[t->type].chains->list,
3a00e5
+			      nft_chain_cmp_byname);
3a00e5
+	h->cache->table[t->type].sorted = true;
3a00e5
+	return 0;
3a00e5
+}
3a00e5
+
3a00e5
 struct nftnl_chain_list_cb_data {
3a00e5
 	struct nft_handle *h;
3a00e5
 	const struct builtin_table *t;
3a00e5
@@ -663,6 +706,7 @@ static int flush_cache(struct nft_handle *h, struct nft_cache *c,
3a00e5
 
3a00e5
 		flush_base_chain_cache(c->table[table->type].base_chains);
3a00e5
 		nft_chain_foreach(h, tablename, __flush_chain_cache, NULL);
3a00e5
+		c->table[table->type].sorted = false;
3a00e5
 
3a00e5
 		if (c->table[table->type].sets)
3a00e5
 			nftnl_set_list_foreach(c->table[table->type].sets,
3a00e5
@@ -678,6 +722,7 @@ static int flush_cache(struct nft_handle *h, struct nft_cache *c,
3a00e5
 		if (c->table[i].chains) {
3a00e5
 			nft_chain_list_free(c->table[i].chains);
3a00e5
 			c->table[i].chains = NULL;
3a00e5
+			c->table[i].sorted = false;
3a00e5
 		}
3a00e5
 
3a00e5
 		if (c->table[i].sets) {
3a00e5
diff --git a/iptables/nft-cache.h b/iptables/nft-cache.h
3a00e5
index 20d96beede876..58a015265056c 100644
3a00e5
--- a/iptables/nft-cache.h
3a00e5
+++ b/iptables/nft-cache.h
3a00e5
@@ -16,6 +16,7 @@ int flush_rule_cache(struct nft_handle *h, const char *table,
3a00e5
 void nft_cache_build(struct nft_handle *h);
3a00e5
 int nft_cache_add_chain(struct nft_handle *h, const struct builtin_table *t,
3a00e5
 			struct nftnl_chain *c);
3a00e5
+int nft_cache_sort_chains(struct nft_handle *h, const char *table);
3a00e5
 
3a00e5
 struct nft_chain *
3a00e5
 nft_chain_find(struct nft_handle *h, const char *table, const char *chain);
3a00e5
diff --git a/iptables/nft.c b/iptables/nft.c
3a00e5
index bde4ca72d3fcc..8b14daeaed610 100644
3a00e5
--- a/iptables/nft.c
3a00e5
+++ b/iptables/nft.c
3a00e5
@@ -1754,6 +1754,8 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
3a00e5
 		return 1;
3a00e5
 	}
3a00e5
 
3a00e5
+	nft_cache_sort_chains(h, table);
3a00e5
+
3a00e5
 	ret = nft_chain_foreach(h, table, nft_rule_flush_cb, &d);
3a00e5
 
3a00e5
 	/* the core expects 1 for success and 0 for error */
3a00e5
@@ -1900,6 +1902,9 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain,
3a00e5
 		goto out;
3a00e5
 	}
3a00e5
 
3a00e5
+	if (verbose)
3a00e5
+		nft_cache_sort_chains(h, table);
3a00e5
+
3a00e5
 	ret = nft_chain_foreach(h, table, __nft_chain_user_del, &d);
3a00e5
 out:
3a00e5
 	/* the core expects 1 for success and 0 for error */
3a00e5
@@ -2437,6 +2442,8 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
3a00e5
 		return 1;
3a00e5
 	}
3a00e5
 
3a00e5
+	nft_cache_sort_chains(h, table);
3a00e5
+
3a00e5
 	if (ops->print_table_header)
3a00e5
 		ops->print_table_header(table);
3a00e5
 
3a00e5
@@ -2540,6 +2547,8 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
3a00e5
 		return nft_rule_list_cb(c, &d);
3a00e5
 	}
3a00e5
 
3a00e5
+	nft_cache_sort_chains(h, table);
3a00e5
+
3a00e5
 	/* Dump policies and custom chains first */
3a00e5
 	nft_chain_foreach(h, table, nft_rule_list_chain_save, &counters);
3a00e5
 
3a00e5
@@ -3431,6 +3440,9 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
3a00e5
 		goto err;
3a00e5
 	}
3a00e5
 
3a00e5
+	if (verbose)
3a00e5
+		nft_cache_sort_chains(h, table);
3a00e5
+
3a00e5
 	ret = nft_chain_foreach(h, table, __nft_chain_zero_counters, &d);
3a00e5
 err:
3a00e5
 	/* the core expects 1 for success and 0 for error */
3a00e5
diff --git a/iptables/nft.h b/iptables/nft.h
3a00e5
index 0910f82a2773c..4ac7e0099d567 100644
3a00e5
--- a/iptables/nft.h
3a00e5
+++ b/iptables/nft.h
3a00e5
@@ -44,6 +44,7 @@ struct nft_cache {
3a00e5
 		struct nft_chain_list	*chains;
3a00e5
 		struct nftnl_set_list	*sets;
3a00e5
 		bool			exists;
3a00e5
+		bool			sorted;
3a00e5
 	} table[NFT_TABLE_MAX];
3a00e5
 };
3a00e5
 
3a00e5
diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c
3a00e5
index d7901c650ea70..cfce0472f3ee8 100644
3a00e5
--- a/iptables/xtables-save.c
3a00e5
+++ b/iptables/xtables-save.c
3a00e5
@@ -87,6 +87,7 @@ __do_output(struct nft_handle *h, const char *tablename, void *data)
3a00e5
 	printf("*%s\n", tablename);
3a00e5
 	/* Dump out chain names first,
3a00e5
 	 * thereby preventing dependency conflicts */
3a00e5
+	nft_cache_sort_chains(h, tablename);
3a00e5
 	nft_chain_foreach(h, tablename, nft_chain_save, h);
3a00e5
 	nft_rule_save(h, tablename, d->format);
3a00e5
 	if (d->commit)
3a00e5
-- 
3a00e5
2.31.1
3a00e5