Skip to content

Commit 2d4a7a0

Browse files
Sebastian Andrzej Siewiorgregkh
authored andcommitted
netfilter: nft_counter: Use u64_stats_t for statistic.
commit 4a1d3acd6ea86075e77fcc1188c3fc372833ba73 upstream. The nft_counter uses two s64 counters for statistics. Those two are protected by a seqcount to ensure that the 64bit variable is always properly seen during updates even on 32bit architectures where the store is performed by two writes. A side effect is that the two counter (bytes and packet) are written and read together in the same window. This can be replaced with u64_stats_t. write_seqcount_begin()/ end() is replaced with u64_stats_update_begin()/ end() and behaves the same way as with seqcount_t on 32bit architectures. Additionally there is a preempt_disable on PREEMPT_RT to ensure that a reader does not preempt a writer. On 64bit architectures the macros are removed and the reads happen without any retries. This also means that the reader can observe one counter (bytes) from before the update and the other counter (packets) but that is okay since there is no requirement to have both counter from the same update window. Convert the statistic to u64_stats_t. There is one optimisation: nft_counter_do_init() and nft_counter_clone() allocate a new per-CPU counter and assign a value to it. During this assignment preemption is disabled which is not needed because the counter is not yet exposed to the system so there can not be another writer or reader. Therefore disabling preemption is omitted and raw_cpu_ptr() is used to obtain a pointer to a counter for the assignment. Cc: Eric Dumazet <[email protected]> Signed-off-by: Sebastian Andrzej Siewior <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]> Signed-off-by: Felix Moessbauer <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 4375eee commit 2d4a7a0

File tree

1 file changed

+46
-44
lines changed

1 file changed

+46
-44
lines changed

net/netfilter/nft_counter.c

Lines changed: 46 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include <linux/kernel.h>
99
#include <linux/init.h>
1010
#include <linux/module.h>
11-
#include <linux/seqlock.h>
11+
#include <linux/u64_stats_sync.h>
1212
#include <linux/netlink.h>
1313
#include <linux/netfilter.h>
1414
#include <linux/netfilter/nf_tables.h>
@@ -17,6 +17,11 @@
1717
#include <net/netfilter/nf_tables_offload.h>
1818

1919
struct nft_counter {
20+
u64_stats_t bytes;
21+
u64_stats_t packets;
22+
};
23+
24+
struct nft_counter_tot {
2025
s64 bytes;
2126
s64 packets;
2227
};
@@ -25,25 +30,24 @@ struct nft_counter_percpu_priv {
2530
struct nft_counter __percpu *counter;
2631
};
2732

28-
static DEFINE_PER_CPU(seqcount_t, nft_counter_seq);
33+
static DEFINE_PER_CPU(struct u64_stats_sync, nft_counter_sync);
2934

3035
static inline void nft_counter_do_eval(struct nft_counter_percpu_priv *priv,
3136
struct nft_regs *regs,
3237
const struct nft_pktinfo *pkt)
3338
{
39+
struct u64_stats_sync *nft_sync;
3440
struct nft_counter *this_cpu;
35-
seqcount_t *myseq;
3641

3742
local_bh_disable();
3843
this_cpu = this_cpu_ptr(priv->counter);
39-
myseq = this_cpu_ptr(&nft_counter_seq);
40-
41-
write_seqcount_begin(myseq);
44+
nft_sync = this_cpu_ptr(&nft_counter_sync);
4245

43-
this_cpu->bytes += pkt->skb->len;
44-
this_cpu->packets++;
46+
u64_stats_update_begin(nft_sync);
47+
u64_stats_add(&this_cpu->bytes, pkt->skb->len);
48+
u64_stats_inc(&this_cpu->packets);
49+
u64_stats_update_end(nft_sync);
4550

46-
write_seqcount_end(myseq);
4751
local_bh_enable();
4852
}
4953

@@ -66,17 +70,16 @@ static int nft_counter_do_init(const struct nlattr * const tb[],
6670
if (cpu_stats == NULL)
6771
return -ENOMEM;
6872

69-
preempt_disable();
70-
this_cpu = this_cpu_ptr(cpu_stats);
73+
this_cpu = raw_cpu_ptr(cpu_stats);
7174
if (tb[NFTA_COUNTER_PACKETS]) {
72-
this_cpu->packets =
73-
be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
75+
u64_stats_set(&this_cpu->packets,
76+
be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])));
7477
}
7578
if (tb[NFTA_COUNTER_BYTES]) {
76-
this_cpu->bytes =
77-
be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
79+
u64_stats_set(&this_cpu->bytes,
80+
be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])));
7881
}
79-
preempt_enable();
82+
8083
priv->counter = cpu_stats;
8184
return 0;
8285
}
@@ -104,40 +107,41 @@ static void nft_counter_obj_destroy(const struct nft_ctx *ctx,
104107
}
105108

106109
static void nft_counter_reset(struct nft_counter_percpu_priv *priv,
107-
struct nft_counter *total)
110+
struct nft_counter_tot *total)
108111
{
112+
struct u64_stats_sync *nft_sync;
109113
struct nft_counter *this_cpu;
110-
seqcount_t *myseq;
111114

112115
local_bh_disable();
113116
this_cpu = this_cpu_ptr(priv->counter);
114-
myseq = this_cpu_ptr(&nft_counter_seq);
117+
nft_sync = this_cpu_ptr(&nft_counter_sync);
118+
119+
u64_stats_update_begin(nft_sync);
120+
u64_stats_add(&this_cpu->packets, -total->packets);
121+
u64_stats_add(&this_cpu->bytes, -total->bytes);
122+
u64_stats_update_end(nft_sync);
115123

116-
write_seqcount_begin(myseq);
117-
this_cpu->packets -= total->packets;
118-
this_cpu->bytes -= total->bytes;
119-
write_seqcount_end(myseq);
120124
local_bh_enable();
121125
}
122126

123127
static void nft_counter_fetch(struct nft_counter_percpu_priv *priv,
124-
struct nft_counter *total)
128+
struct nft_counter_tot *total)
125129
{
126130
struct nft_counter *this_cpu;
127-
const seqcount_t *myseq;
128131
u64 bytes, packets;
129132
unsigned int seq;
130133
int cpu;
131134

132135
memset(total, 0, sizeof(*total));
133136
for_each_possible_cpu(cpu) {
134-
myseq = per_cpu_ptr(&nft_counter_seq, cpu);
137+
struct u64_stats_sync *nft_sync = per_cpu_ptr(&nft_counter_sync, cpu);
138+
135139
this_cpu = per_cpu_ptr(priv->counter, cpu);
136140
do {
137-
seq = read_seqcount_begin(myseq);
138-
bytes = this_cpu->bytes;
139-
packets = this_cpu->packets;
140-
} while (read_seqcount_retry(myseq, seq));
141+
seq = u64_stats_fetch_begin(nft_sync);
142+
bytes = u64_stats_read(&this_cpu->bytes);
143+
packets = u64_stats_read(&this_cpu->packets);
144+
} while (u64_stats_fetch_retry(nft_sync, seq));
141145

142146
total->bytes += bytes;
143147
total->packets += packets;
@@ -148,7 +152,7 @@ static int nft_counter_do_dump(struct sk_buff *skb,
148152
struct nft_counter_percpu_priv *priv,
149153
bool reset)
150154
{
151-
struct nft_counter total;
155+
struct nft_counter_tot total;
152156

153157
nft_counter_fetch(priv, &total);
154158

@@ -236,19 +240,17 @@ static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src, g
236240
struct nft_counter_percpu_priv *priv_clone = nft_expr_priv(dst);
237241
struct nft_counter __percpu *cpu_stats;
238242
struct nft_counter *this_cpu;
239-
struct nft_counter total;
243+
struct nft_counter_tot total;
240244

241245
nft_counter_fetch(priv, &total);
242246

243247
cpu_stats = alloc_percpu_gfp(struct nft_counter, gfp);
244248
if (cpu_stats == NULL)
245249
return -ENOMEM;
246250

247-
preempt_disable();
248-
this_cpu = this_cpu_ptr(cpu_stats);
249-
this_cpu->packets = total.packets;
250-
this_cpu->bytes = total.bytes;
251-
preempt_enable();
251+
this_cpu = raw_cpu_ptr(cpu_stats);
252+
u64_stats_set(&this_cpu->packets, total.packets);
253+
u64_stats_set(&this_cpu->bytes, total.bytes);
252254

253255
priv_clone->counter = cpu_stats;
254256
return 0;
@@ -266,17 +268,17 @@ static void nft_counter_offload_stats(struct nft_expr *expr,
266268
const struct flow_stats *stats)
267269
{
268270
struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
271+
struct u64_stats_sync *nft_sync;
269272
struct nft_counter *this_cpu;
270-
seqcount_t *myseq;
271273

272274
local_bh_disable();
273275
this_cpu = this_cpu_ptr(priv->counter);
274-
myseq = this_cpu_ptr(&nft_counter_seq);
276+
nft_sync = this_cpu_ptr(&nft_counter_sync);
275277

276-
write_seqcount_begin(myseq);
277-
this_cpu->packets += stats->pkts;
278-
this_cpu->bytes += stats->bytes;
279-
write_seqcount_end(myseq);
278+
u64_stats_update_begin(nft_sync);
279+
u64_stats_add(&this_cpu->packets, stats->pkts);
280+
u64_stats_add(&this_cpu->bytes, stats->bytes);
281+
u64_stats_update_end(nft_sync);
280282
local_bh_enable();
281283
}
282284

@@ -285,7 +287,7 @@ void nft_counter_init_seqcount(void)
285287
int cpu;
286288

287289
for_each_possible_cpu(cpu)
288-
seqcount_init(per_cpu_ptr(&nft_counter_seq, cpu));
290+
u64_stats_init(per_cpu_ptr(&nft_counter_sync, cpu));
289291
}
290292

291293
struct nft_expr_type nft_counter_type;

0 commit comments

Comments
 (0)