Skip to content

Commit ce355e2

Browse files
edumazetummakynes
authored andcommitted
netfilter: nf_tables: 64bit stats need some extra synchronization
Use generic u64_stats_sync infrastructure to get proper 64bit stats, even on 32bit arches, at no extra cost for 64bit arches. Without this fix, 32bit arches can have some wrong counters at the time the carry is propagated into upper word. Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 38e029f commit ce355e2

File tree

3 files changed

+21
-10
lines changed

3 files changed

+21
-10
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/netfilter/nfnetlink.h>
77
#include <linux/netfilter/x_tables.h>
88
#include <linux/netfilter/nf_tables.h>
9+
#include <linux/u64_stats_sync.h>
910
#include <net/netlink.h>
1011

1112
#define NFT_JUMP_STACK_SIZE 16
@@ -528,8 +529,9 @@ enum nft_chain_type {
528529
};
529530

530531
struct nft_stats {
531-
u64 bytes;
532-
u64 pkts;
532+
u64 bytes;
533+
u64 pkts;
534+
struct u64_stats_sync syncp;
533535
};
534536

535537
#define NFT_HOOK_OPS_MAX 2

net/netfilter/nf_tables_api.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -644,13 +644,20 @@ static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats)
644644
{
645645
struct nft_stats *cpu_stats, total;
646646
struct nlattr *nest;
647+
unsigned int seq;
648+
u64 pkts, bytes;
647649
int cpu;
648650

649651
memset(&total, 0, sizeof(total));
650652
for_each_possible_cpu(cpu) {
651653
cpu_stats = per_cpu_ptr(stats, cpu);
652-
total.pkts += cpu_stats->pkts;
653-
total.bytes += cpu_stats->bytes;
654+
do {
655+
seq = u64_stats_fetch_begin_irq(&cpu_stats->syncp);
656+
pkts = cpu_stats->pkts;
657+
bytes = cpu_stats->bytes;
658+
} while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, seq));
659+
total.pkts += pkts;
660+
total.bytes += bytes;
654661
}
655662
nest = nla_nest_start(skb, NFTA_CHAIN_COUNTERS);
656663
if (nest == NULL)
@@ -875,7 +882,7 @@ static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
875882
if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS])
876883
return ERR_PTR(-EINVAL);
877884

878-
newstats = alloc_percpu(struct nft_stats);
885+
newstats = netdev_alloc_pcpu_stats(struct nft_stats);
879886
if (newstats == NULL)
880887
return ERR_PTR(-ENOMEM);
881888

@@ -1091,7 +1098,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
10911098
}
10921099
basechain->stats = stats;
10931100
} else {
1094-
stats = alloc_percpu(struct nft_stats);
1101+
stats = netdev_alloc_pcpu_stats(struct nft_stats);
10951102
if (IS_ERR(stats)) {
10961103
module_put(type->owner);
10971104
kfree(basechain);

net/netfilter/nf_tables_core.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)
109109
struct nft_data data[NFT_REG_MAX + 1];
110110
unsigned int stackptr = 0;
111111
struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE];
112-
struct nft_stats __percpu *stats;
112+
struct nft_stats *stats;
113113
int rulenum;
114114
/*
115115
* Cache cursor to avoid problems in case that the cursor is updated
@@ -205,9 +205,11 @@ nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)
205205
nft_trace_packet(pkt, basechain, -1, NFT_TRACE_POLICY);
206206

207207
rcu_read_lock_bh();
208-
stats = rcu_dereference(nft_base_chain(basechain)->stats);
209-
__this_cpu_inc(stats->pkts);
210-
__this_cpu_add(stats->bytes, pkt->skb->len);
208+
stats = this_cpu_ptr(rcu_dereference(nft_base_chain(basechain)->stats));
209+
u64_stats_update_begin(&stats->syncp);
210+
stats->pkts++;
211+
stats->bytes += pkt->skb->len;
212+
u64_stats_update_end(&stats->syncp);
211213
rcu_read_unlock_bh();
212214

213215
return nft_base_chain(basechain)->policy;

0 commit comments

Comments
 (0)