From fbd8fb09c50bcee3f046dce2281f25baa4e14927 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 21 Aug 2020 12:04:12 +0200 Subject: src: add chain hashtable cache This significantly improves ruleset listing time with large rulesets (~50k rules) with _lots_ of non-base chains. # time nft list ruleset &> /dev/null Before this patch: real 0m11,172s user 0m6,810s sys 0m4,220s After this patch: real 0m4,747s user 0m0,802s sys 0m3,912s This patch also removes list_bindings from netlink_ctx since there is no need to keep a temporary list of chains anymore. Signed-off-by: Pablo Neira Ayuso --- src/cache.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) (limited to 'src/cache.c') diff --git a/src/cache.c b/src/cache.c index 7797ff6b..ed260900 100644 --- a/src/cache.c +++ b/src/cache.c @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags) { @@ -164,3 +167,92 @@ unsigned int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds) return flags; } + +struct chain_cache_dump_ctx { + struct netlink_ctx *nlctx; + struct table *table; +}; + +static int chain_cache_cb(struct nftnl_chain *nlc, void *arg) +{ + struct chain_cache_dump_ctx *ctx = arg; + const char *chain_name, *table_name; + uint32_t hash, family; + struct chain *chain; + + table_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE); + chain_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME); + family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY); + + if (strcmp(table_name, ctx->table->handle.table.name) || + family != ctx->table->handle.family) + return 0; + + hash = djb_hash(chain_name) % NFT_CACHE_HSIZE; + chain = netlink_delinearize_chain(ctx->nlctx, nlc); + + if (chain->flags & CHAIN_F_BINDING) { + list_add_tail(&chain->list, &ctx->table->chain_bindings); + } else { + list_add_tail(&chain->hlist, &ctx->table->chain_htable[hash]); + list_add_tail(&chain->list, &ctx->table->chains); + } + + nftnl_chain_list_del(nlc); + nftnl_chain_free(nlc); + + return 0; +} + +int chain_cache_init(struct netlink_ctx *ctx, struct table *table, + struct nftnl_chain_list *chain_list) +{ + struct chain_cache_dump_ctx dump_ctx = { + .nlctx = ctx, + .table = table, + }; + nftnl_chain_list_foreach(chain_list, chain_cache_cb, &dump_ctx); + + return 0; +} + +struct nftnl_chain_list *chain_cache_dump(struct netlink_ctx *ctx, int *err) +{ + struct nftnl_chain_list *chain_list; + + chain_list = mnl_nft_chain_dump(ctx, AF_UNSPEC); + if (chain_list == NULL) { + if (errno == EINTR) { + *err = -1; + return NULL; + } + *err = 0; + return NULL; + } + + return chain_list; +} + +void chain_cache_add(struct chain *chain, struct table *table) +{ + uint32_t hash; + + hash = djb_hash(chain->handle.chain.name) % NFT_CACHE_HSIZE; + list_add_tail(&chain->hlist, &table->chain_htable[hash]); + list_add_tail(&chain->list, &table->chains); +} + +struct chain *chain_cache_find(const struct table *table, + const struct handle *handle) +{ + struct chain *chain; + uint32_t hash; + + hash = djb_hash(handle->chain.name) % NFT_CACHE_HSIZE; + list_for_each_entry(chain, &table->chain_htable[hash], hlist) { + if (!strcmp(chain->handle.chain.name, handle->chain.name)) + return chain; + } + + return NULL; +} -- cgit v1.2.3