]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
nft: cache: Sort chains on demand only
authorPhil Sutter <phil@nwl.cc>
Thu, 25 Mar 2021 15:24:39 +0000 (16:24 +0100)
committerPhil Sutter <phil@nwl.cc>
Tue, 6 Apr 2021 08:52:42 +0000 (10:52 +0200)
Mandatory sorted insert of chains into cache significantly slows down
restoring of large rulesets. Since the sorted list of user-defined
chains is needed for listing and verbose output only, introduce
nft_cache_sort_chains() and call it where needed.

Signed-off-by: Phil Sutter <phil@nwl.cc>
iptables/nft-cache.c
iptables/nft-cache.h
iptables/nft.c
iptables/nft.h
iptables/xtables-save.c

index 6b6e6da40a82629845c4c41990f364c8e455d86a..8fbf9727826d85512951cbd86a7cf4933c458371 100644 (file)
@@ -223,24 +223,67 @@ int nft_cache_add_chain(struct nft_handle *h, const struct builtin_table *t,
 
                h->cache->table[t->type].base_chains[hooknum] = nc;
        } else {
-               struct nft_chain_list *clist = h->cache->table[t->type].chains;
-               struct list_head *pos = &clist->list;
-               struct nft_chain *cur;
-               const char *n;
-
-               list_for_each_entry(cur, &clist->list, head) {
-                       n = nftnl_chain_get_str(cur->nftnl, NFTNL_CHAIN_NAME);
-                       if (strcmp(cname, n) <= 0) {
-                               pos = &cur->head;
-                               break;
-                       }
-               }
-               list_add_tail(&nc->head, pos);
+               list_add_tail(&nc->head,
+                             &h->cache->table[t->type].chains->list);
        }
        hlist_add_head(&nc->hnode, chain_name_hlist(h, t, cname));
        return 0;
 }
 
+static void __nft_chain_list_sort(struct list_head *list,
+                                 int (*cmp)(struct nft_chain *a,
+                                            struct nft_chain *b))
+{
+       struct nft_chain *pivot, *cur, *sav;
+       LIST_HEAD(sublist);
+
+       if (list_empty(list))
+               return;
+
+       /* grab first item as pivot (dividing) value */
+       pivot = list_entry(list->next, struct nft_chain, head);
+       list_del(&pivot->head);
+
+       /* move any smaller value into sublist */
+       list_for_each_entry_safe(cur, sav, list, head) {
+               if (cmp(pivot, cur) > 0) {
+                       list_del(&cur->head);
+                       list_add_tail(&cur->head, &sublist);
+               }
+       }
+       /* conquer divided */
+       __nft_chain_list_sort(&sublist, cmp);
+       __nft_chain_list_sort(list, cmp);
+
+       /* merge divided and pivot again */
+       list_add_tail(&pivot->head, &sublist);
+       list_splice(&sublist, list);
+}
+
+static int nft_chain_cmp_byname(struct nft_chain *a, struct nft_chain *b)
+{
+       const char *aname = nftnl_chain_get_str(a->nftnl, NFTNL_CHAIN_NAME);
+       const char *bname = nftnl_chain_get_str(b->nftnl, NFTNL_CHAIN_NAME);
+
+       return strcmp(aname, bname);
+}
+
+int nft_cache_sort_chains(struct nft_handle *h, const char *table)
+{
+       const struct builtin_table *t = nft_table_builtin_find(h, table);
+
+       if (!t)
+               return -1;
+
+       if (h->cache->table[t->type].sorted)
+               return 0;
+
+       __nft_chain_list_sort(&h->cache->table[t->type].chains->list,
+                             nft_chain_cmp_byname);
+       h->cache->table[t->type].sorted = true;
+       return 0;
+}
+
 struct nftnl_chain_list_cb_data {
        struct nft_handle *h;
        const struct builtin_table *t;
@@ -663,6 +706,7 @@ static int flush_cache(struct nft_handle *h, struct nft_cache *c,
 
                flush_base_chain_cache(c->table[table->type].base_chains);
                nft_chain_foreach(h, tablename, __flush_chain_cache, NULL);
+               c->table[table->type].sorted = false;
 
                if (c->table[table->type].sets)
                        nftnl_set_list_foreach(c->table[table->type].sets,
@@ -678,6 +722,7 @@ static int flush_cache(struct nft_handle *h, struct nft_cache *c,
                if (c->table[i].chains) {
                        nft_chain_list_free(c->table[i].chains);
                        c->table[i].chains = NULL;
+                       c->table[i].sorted = false;
                }
 
                if (c->table[i].sets) {
index 20d96beede876b4a80354cc2097d8dc319f27401..58a015265056c883795da8220aa32382f773b293 100644 (file)
@@ -16,6 +16,7 @@ int flush_rule_cache(struct nft_handle *h, const char *table,
 void nft_cache_build(struct nft_handle *h);
 int nft_cache_add_chain(struct nft_handle *h, const struct builtin_table *t,
                        struct nftnl_chain *c);
+int nft_cache_sort_chains(struct nft_handle *h, const char *table);
 
 struct nft_chain *
 nft_chain_find(struct nft_handle *h, const char *table, const char *chain);
index bde4ca72d3fcc8555fb6c3034b1aeec68406d904..8b14daeaed61035e048f127a2bbd98ed28adb58c 100644 (file)
@@ -1754,6 +1754,8 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
                return 1;
        }
 
+       nft_cache_sort_chains(h, table);
+
        ret = nft_chain_foreach(h, table, nft_rule_flush_cb, &d);
 
        /* the core expects 1 for success and 0 for error */
@@ -1900,6 +1902,9 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain,
                goto out;
        }
 
+       if (verbose)
+               nft_cache_sort_chains(h, table);
+
        ret = nft_chain_foreach(h, table, __nft_chain_user_del, &d);
 out:
        /* the core expects 1 for success and 0 for error */
@@ -2437,6 +2442,8 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
                return 1;
        }
 
+       nft_cache_sort_chains(h, table);
+
        if (ops->print_table_header)
                ops->print_table_header(table);
 
@@ -2540,6 +2547,8 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
                return nft_rule_list_cb(c, &d);
        }
 
+       nft_cache_sort_chains(h, table);
+
        /* Dump policies and custom chains first */
        nft_chain_foreach(h, table, nft_rule_list_chain_save, &counters);
 
@@ -3431,6 +3440,9 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
                goto err;
        }
 
+       if (verbose)
+               nft_cache_sort_chains(h, table);
+
        ret = nft_chain_foreach(h, table, __nft_chain_zero_counters, &d);
 err:
        /* the core expects 1 for success and 0 for error */
index 0910f82a2773c25112678b5465512206a56321b9..4ac7e0099d5670a3ff33db5d1136dc8fbdb94706 100644 (file)
@@ -44,6 +44,7 @@ struct nft_cache {
                struct nft_chain_list   *chains;
                struct nftnl_set_list   *sets;
                bool                    exists;
+               bool                    sorted;
        } table[NFT_TABLE_MAX];
 };
 
index d7901c650ea70e42d1060090578772f4286fcf6f..cfce0472f3ee813769891b32e1b4af9b07ea2c25 100644 (file)
@@ -87,6 +87,7 @@ __do_output(struct nft_handle *h, const char *tablename, void *data)
        printf("*%s\n", tablename);
        /* Dump out chain names first,
         * thereby preventing dependency conflicts */
+       nft_cache_sort_chains(h, tablename);
        nft_chain_foreach(h, tablename, nft_chain_save, h);
        nft_rule_save(h, tablename, d->format);
        if (d->commit)