mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 10:00:17 +00:00
netfilter: nf_tables: store new sets in dedicated list
[ Upstream commitc1aa38866b] 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:958bee14d0("netfilter: nf_tables: use new transaction infrastructure to handle sets") Reported-by: Nadia Pinaeva <n.m.pinaeva@gmail.com> Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
eac1568213
commit
c4d6be474a
@@ -1674,6 +1674,7 @@ struct nft_trans_rule {
|
|||||||
|
|
||||||
struct nft_trans_set {
|
struct nft_trans_set {
|
||||||
struct nft_trans_binding nft_trans_binding;
|
struct nft_trans_binding nft_trans_binding;
|
||||||
|
struct list_head list_trans_newset;
|
||||||
struct nft_set *set;
|
struct nft_set *set;
|
||||||
u32 set_id;
|
u32 set_id;
|
||||||
u32 gc_int;
|
u32 gc_int;
|
||||||
@@ -1875,6 +1876,7 @@ static inline int nft_request_module(struct net *net, const char *fmt, ...) { re
|
|||||||
struct nftables_pernet {
|
struct nftables_pernet {
|
||||||
struct list_head tables;
|
struct list_head tables;
|
||||||
struct list_head commit_list;
|
struct list_head commit_list;
|
||||||
|
struct list_head commit_set_list;
|
||||||
struct list_head binding_list;
|
struct list_head binding_list;
|
||||||
struct list_head module_list;
|
struct list_head module_list;
|
||||||
struct list_head notify_list;
|
struct list_head notify_list;
|
||||||
|
|||||||
@@ -393,6 +393,7 @@ static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *tr
|
|||||||
{
|
{
|
||||||
struct nftables_pernet *nft_net = nft_pernet(net);
|
struct nftables_pernet *nft_net = nft_pernet(net);
|
||||||
struct nft_trans_binding *binding;
|
struct nft_trans_binding *binding;
|
||||||
|
struct nft_trans_set *trans_set;
|
||||||
|
|
||||||
list_add_tail(&trans->list, &nft_net->commit_list);
|
list_add_tail(&trans->list, &nft_net->commit_list);
|
||||||
|
|
||||||
@@ -402,9 +403,13 @@ static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *tr
|
|||||||
|
|
||||||
switch (trans->msg_type) {
|
switch (trans->msg_type) {
|
||||||
case NFT_MSG_NEWSET:
|
case NFT_MSG_NEWSET:
|
||||||
|
trans_set = nft_trans_container_set(trans);
|
||||||
|
|
||||||
if (!nft_trans_set_update(trans) &&
|
if (!nft_trans_set_update(trans) &&
|
||||||
nft_set_is_anonymous(nft_trans_set(trans)))
|
nft_set_is_anonymous(nft_trans_set(trans)))
|
||||||
list_add_tail(&binding->binding_list, &nft_net->binding_list);
|
list_add_tail(&binding->binding_list, &nft_net->binding_list);
|
||||||
|
|
||||||
|
list_add_tail(&trans_set->list_trans_newset, &nft_net->commit_set_list);
|
||||||
break;
|
break;
|
||||||
case NFT_MSG_NEWCHAIN:
|
case NFT_MSG_NEWCHAIN:
|
||||||
if (!nft_trans_chain_update(trans) &&
|
if (!nft_trans_chain_update(trans) &&
|
||||||
@@ -611,6 +616,7 @@ static int __nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
|
|||||||
|
|
||||||
trans_set = nft_trans_container_set(trans);
|
trans_set = nft_trans_container_set(trans);
|
||||||
INIT_LIST_HEAD(&trans_set->nft_trans_binding.binding_list);
|
INIT_LIST_HEAD(&trans_set->nft_trans_binding.binding_list);
|
||||||
|
INIT_LIST_HEAD(&trans_set->list_trans_newset);
|
||||||
|
|
||||||
if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] && !desc) {
|
if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] && !desc) {
|
||||||
nft_trans_set_id(trans) =
|
nft_trans_set_id(trans) =
|
||||||
@@ -4485,17 +4491,16 @@ static struct nft_set *nft_set_lookup_byid(const struct net *net,
|
|||||||
{
|
{
|
||||||
struct nftables_pernet *nft_net = nft_pernet(net);
|
struct nftables_pernet *nft_net = nft_pernet(net);
|
||||||
u32 id = ntohl(nla_get_be32(nla));
|
u32 id = ntohl(nla_get_be32(nla));
|
||||||
struct nft_trans *trans;
|
struct nft_trans_set *trans;
|
||||||
|
|
||||||
list_for_each_entry(trans, &nft_net->commit_list, list) {
|
/* its likely the id we need is at the tail, not at start */
|
||||||
if (trans->msg_type == NFT_MSG_NEWSET) {
|
list_for_each_entry_reverse(trans, &nft_net->commit_set_list, list_trans_newset) {
|
||||||
struct nft_set *set = nft_trans_set(trans);
|
struct nft_set *set = trans->set;
|
||||||
|
|
||||||
if (id == nft_trans_set_id(trans) &&
|
if (id == trans->set_id &&
|
||||||
set->table == table &&
|
set->table == table &&
|
||||||
nft_active_genmask(set, genmask))
|
nft_active_genmask(set, genmask))
|
||||||
return set;
|
return set;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ERR_PTR(-ENOENT);
|
return ERR_PTR(-ENOENT);
|
||||||
}
|
}
|
||||||
@@ -10447,6 +10452,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
|||||||
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
|
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
|
||||||
break;
|
break;
|
||||||
case NFT_MSG_NEWSET:
|
case NFT_MSG_NEWSET:
|
||||||
|
list_del(&nft_trans_container_set(trans)->list_trans_newset);
|
||||||
if (nft_trans_set_update(trans)) {
|
if (nft_trans_set_update(trans)) {
|
||||||
struct nft_set *set = nft_trans_set(trans);
|
struct nft_set *set = nft_trans_set(trans);
|
||||||
|
|
||||||
@@ -10755,6 +10761,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
|
|||||||
nft_trans_destroy(trans);
|
nft_trans_destroy(trans);
|
||||||
break;
|
break;
|
||||||
case NFT_MSG_NEWSET:
|
case NFT_MSG_NEWSET:
|
||||||
|
list_del(&nft_trans_container_set(trans)->list_trans_newset);
|
||||||
if (nft_trans_set_update(trans)) {
|
if (nft_trans_set_update(trans)) {
|
||||||
nft_trans_destroy(trans);
|
nft_trans_destroy(trans);
|
||||||
break;
|
break;
|
||||||
@@ -10850,6 +10857,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WARN_ON_ONCE(!list_empty(&nft_net->commit_set_list));
|
||||||
|
|
||||||
nft_set_abort_update(&set_update_list);
|
nft_set_abort_update(&set_update_list);
|
||||||
|
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
@@ -11519,6 +11528,7 @@ static int __net_init nf_tables_init_net(struct net *net)
|
|||||||
|
|
||||||
INIT_LIST_HEAD(&nft_net->tables);
|
INIT_LIST_HEAD(&nft_net->tables);
|
||||||
INIT_LIST_HEAD(&nft_net->commit_list);
|
INIT_LIST_HEAD(&nft_net->commit_list);
|
||||||
|
INIT_LIST_HEAD(&nft_net->commit_set_list);
|
||||||
INIT_LIST_HEAD(&nft_net->binding_list);
|
INIT_LIST_HEAD(&nft_net->binding_list);
|
||||||
INIT_LIST_HEAD(&nft_net->module_list);
|
INIT_LIST_HEAD(&nft_net->module_list);
|
||||||
INIT_LIST_HEAD(&nft_net->notify_list);
|
INIT_LIST_HEAD(&nft_net->notify_list);
|
||||||
@@ -11549,6 +11559,7 @@ static void __net_exit nf_tables_exit_net(struct net *net)
|
|||||||
gc_seq = nft_gc_seq_begin(nft_net);
|
gc_seq = nft_gc_seq_begin(nft_net);
|
||||||
|
|
||||||
WARN_ON_ONCE(!list_empty(&nft_net->commit_list));
|
WARN_ON_ONCE(!list_empty(&nft_net->commit_list));
|
||||||
|
WARN_ON_ONCE(!list_empty(&nft_net->commit_set_list));
|
||||||
|
|
||||||
if (!list_empty(&nft_net->module_list))
|
if (!list_empty(&nft_net->module_list))
|
||||||
nf_tables_module_autoload_cleanup(net);
|
nf_tables_module_autoload_cleanup(net);
|
||||||
|
|||||||
Reference in New Issue
Block a user