Skip to content

Commit c4d6be4

Browse files
Florian Westphalgregkh
Florian Westphal
authored andcommitted
netfilter: nf_tables: store new sets in dedicated list
[ Upstream commit c1aa388 ] nft_set_lookup_byid() is very slow when transaction becomes large, due to walk of the transaction list. Add a dedicated list that contains only the new sets. Before: nft -f ruleset 0.07s user 0.00s system 0% cpu 1:04.84 total After: nft -f ruleset 0.07s user 0.00s system 0% cpu 30.115 total .. where ruleset contains ~10 sets with ~100k elements. The above number is for a combined flush+reload of the ruleset. With previous flush, even the first NEWELEM has to walk through a few hundred thousands of DELSET(ELEM) transactions before the first NEWSET object. To cope with random-order-newset-newsetelem we'd need to replace commit_set_list with a hashtable. Expectation is that a NEWELEM operation refers to the most recently added set, so last entry of the dedicated list should be the set we want. NB: This is not a bug fix per se (functionality is fine), but with larger transaction batches list search takes forever, so it would be nice to speed this up for -stable too, hence adding a "fixes" tag. Fixes: 958bee1 ("netfilter: nf_tables: use new transaction infrastructure to handle sets") Reported-by: Nadia Pinaeva <[email protected]> Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent eac1568 commit c4d6be4

File tree

2 files changed

+22
-9
lines changed

2 files changed

+22
-9
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,6 +1674,7 @@ struct nft_trans_rule {
16741674

16751675
struct nft_trans_set {
16761676
struct nft_trans_binding nft_trans_binding;
1677+
struct list_head list_trans_newset;
16771678
struct nft_set *set;
16781679
u32 set_id;
16791680
u32 gc_int;
@@ -1875,6 +1876,7 @@ static inline int nft_request_module(struct net *net, const char *fmt, ...) { re
18751876
struct nftables_pernet {
18761877
struct list_head tables;
18771878
struct list_head commit_list;
1879+
struct list_head commit_set_list;
18781880
struct list_head binding_list;
18791881
struct list_head module_list;
18801882
struct list_head notify_list;

net/netfilter/nf_tables_api.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *tr
393393
{
394394
struct nftables_pernet *nft_net = nft_pernet(net);
395395
struct nft_trans_binding *binding;
396+
struct nft_trans_set *trans_set;
396397

397398
list_add_tail(&trans->list, &nft_net->commit_list);
398399

@@ -402,9 +403,13 @@ static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *tr
402403

403404
switch (trans->msg_type) {
404405
case NFT_MSG_NEWSET:
406+
trans_set = nft_trans_container_set(trans);
407+
405408
if (!nft_trans_set_update(trans) &&
406409
nft_set_is_anonymous(nft_trans_set(trans)))
407410
list_add_tail(&binding->binding_list, &nft_net->binding_list);
411+
412+
list_add_tail(&trans_set->list_trans_newset, &nft_net->commit_set_list);
408413
break;
409414
case NFT_MSG_NEWCHAIN:
410415
if (!nft_trans_chain_update(trans) &&
@@ -611,6 +616,7 @@ static int __nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
611616

612617
trans_set = nft_trans_container_set(trans);
613618
INIT_LIST_HEAD(&trans_set->nft_trans_binding.binding_list);
619+
INIT_LIST_HEAD(&trans_set->list_trans_newset);
614620

615621
if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] && !desc) {
616622
nft_trans_set_id(trans) =
@@ -4485,17 +4491,16 @@ static struct nft_set *nft_set_lookup_byid(const struct net *net,
44854491
{
44864492
struct nftables_pernet *nft_net = nft_pernet(net);
44874493
u32 id = ntohl(nla_get_be32(nla));
4488-
struct nft_trans *trans;
4494+
struct nft_trans_set *trans;
44894495

4490-
list_for_each_entry(trans, &nft_net->commit_list, list) {
4491-
if (trans->msg_type == NFT_MSG_NEWSET) {
4492-
struct nft_set *set = nft_trans_set(trans);
4496+
/* its likely the id we need is at the tail, not at start */
4497+
list_for_each_entry_reverse(trans, &nft_net->commit_set_list, list_trans_newset) {
4498+
struct nft_set *set = trans->set;
44934499

4494-
if (id == nft_trans_set_id(trans) &&
4495-
set->table == table &&
4496-
nft_active_genmask(set, genmask))
4497-
return set;
4498-
}
4500+
if (id == trans->set_id &&
4501+
set->table == table &&
4502+
nft_active_genmask(set, genmask))
4503+
return set;
44994504
}
45004505
return ERR_PTR(-ENOENT);
45014506
}
@@ -10447,6 +10452,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
1044710452
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
1044810453
break;
1044910454
case NFT_MSG_NEWSET:
10455+
list_del(&nft_trans_container_set(trans)->list_trans_newset);
1045010456
if (nft_trans_set_update(trans)) {
1045110457
struct nft_set *set = nft_trans_set(trans);
1045210458

@@ -10755,6 +10761,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
1075510761
nft_trans_destroy(trans);
1075610762
break;
1075710763
case NFT_MSG_NEWSET:
10764+
list_del(&nft_trans_container_set(trans)->list_trans_newset);
1075810765
if (nft_trans_set_update(trans)) {
1075910766
nft_trans_destroy(trans);
1076010767
break;
@@ -10850,6 +10857,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
1085010857
}
1085110858
}
1085210859

10860+
WARN_ON_ONCE(!list_empty(&nft_net->commit_set_list));
10861+
1085310862
nft_set_abort_update(&set_update_list);
1085410863

1085510864
synchronize_rcu();
@@ -11519,6 +11528,7 @@ static int __net_init nf_tables_init_net(struct net *net)
1151911528

1152011529
INIT_LIST_HEAD(&nft_net->tables);
1152111530
INIT_LIST_HEAD(&nft_net->commit_list);
11531+
INIT_LIST_HEAD(&nft_net->commit_set_list);
1152211532
INIT_LIST_HEAD(&nft_net->binding_list);
1152311533
INIT_LIST_HEAD(&nft_net->module_list);
1152411534
INIT_LIST_HEAD(&nft_net->notify_list);
@@ -11549,6 +11559,7 @@ static void __net_exit nf_tables_exit_net(struct net *net)
1154911559
gc_seq = nft_gc_seq_begin(nft_net);
1155011560

1155111561
WARN_ON_ONCE(!list_empty(&nft_net->commit_list));
11562+
WARN_ON_ONCE(!list_empty(&nft_net->commit_set_list));
1155211563

1155311564
if (!list_empty(&nft_net->module_list))
1155411565
nf_tables_module_autoload_cleanup(net);

0 commit comments

Comments
 (0)