netfilter: nf_tables: free base chain counters from worker
authorFlorian Westphal <fw@strlen.de>
Wed, 22 May 2019 21:35:11 +0000 (23:35 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 31 May 2019 16:02:43 +0000 (18:02 +0200)
No need to use synchronize_rcu() here, just swap the two pointers
and have the release occur from work queue after commit has completed.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/netfilter/nf_tables_api.c

index 28241e8..2fed78b 100644 (file)
@@ -1449,25 +1449,18 @@ static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
        return newstats;
 }
 
-static void nft_chain_stats_replace(struct net *net,
-                                   struct nft_base_chain *chain,
-                                   struct nft_stats __percpu *newstats)
+static void nft_chain_stats_replace(struct nft_trans *trans)
 {
-       struct nft_stats __percpu *oldstats;
+       struct nft_base_chain *chain = nft_base_chain(trans->ctx.chain);
 
-       if (newstats == NULL)
+       if (!nft_trans_chain_stats(trans))
                return;
 
-       if (rcu_access_pointer(chain->stats)) {
-               oldstats = rcu_dereference_protected(chain->stats,
-                                       lockdep_commit_lock_is_held(net));
-               rcu_assign_pointer(chain->stats, newstats);
-               synchronize_rcu();
-               free_percpu(oldstats);
-       } else {
-               rcu_assign_pointer(chain->stats, newstats);
+       rcu_swap_protected(chain->stats, nft_trans_chain_stats(trans),
+                          lockdep_commit_lock_is_held(trans->ctx.net));
+
+       if (!nft_trans_chain_stats(trans))
                static_branch_inc(&nft_counters_enabled);
-       }
 }
 
 static void nf_tables_chain_free_chain_rules(struct nft_chain *chain)
@@ -6360,9 +6353,9 @@ static void nft_chain_commit_update(struct nft_trans *trans)
        if (!nft_is_base_chain(trans->ctx.chain))
                return;
 
+       nft_chain_stats_replace(trans);
+
        basechain = nft_base_chain(trans->ctx.chain);
-       nft_chain_stats_replace(trans->ctx.net, basechain,
-                               nft_trans_chain_stats(trans));
 
        switch (nft_trans_chain_policy(trans)) {
        case NF_DROP:
@@ -6379,6 +6372,7 @@ static void nft_commit_release(struct nft_trans *trans)
                nf_tables_table_destroy(&trans->ctx);
                break;
        case NFT_MSG_NEWCHAIN:
+               free_percpu(nft_trans_chain_stats(trans));
                kfree(nft_trans_chain_name(trans));
                break;
        case NFT_MSG_DELCHAIN: