kernel: fix mtk flow offload list corruption issue with l2 flows
[openwrt/staging/dedeckeh.git] / target / linux / generic / pending-5.15 / 736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch
1 From: Felix Fietkau <nbd@nbd.name>
2 Date: Thu, 23 Mar 2023 11:05:22 +0100
3 Subject: [PATCH] net: ethernet: mediatek: fix ppe flow accounting for L2
4 flows
5
6 For L2 flows, the packet/byte counters should report the sum of the
7 counters of their subflows, both current and expired.
8 In order to make this work, change the way that accounting data is tracked.
9 Reset counters when a flow enters bind. Once it expires (or enters unbind),
10 store the last counter value in struct mtk_flow_entry.
11
12 Signed-off-by: Felix Fietkau <nbd@nbd.name>
13 ---
14
15 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
16 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
17 @@ -80,9 +80,9 @@ static int mtk_ppe_mib_wait_busy(struct
18 int ret;
19 u32 val;
20
21 - ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val,
22 - !(val & MTK_PPE_MIB_SER_CR_ST),
23 - 20, MTK_PPE_WAIT_TIMEOUT_US);
24 + ret = readl_poll_timeout_atomic(ppe->base + MTK_PPE_MIB_SER_CR, val,
25 + !(val & MTK_PPE_MIB_SER_CR_ST),
26 + 20, MTK_PPE_WAIT_TIMEOUT_US);
27
28 if (ret)
29 dev_err(ppe->dev, "MIB table busy");
30 @@ -90,18 +90,32 @@ static int mtk_ppe_mib_wait_busy(struct
31 return ret;
32 }
33
34 -static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
35 +static inline struct mtk_foe_accounting *
36 +mtk_ppe_acct_data(struct mtk_ppe *ppe, u16 index)
37 +{
38 + if (!ppe->acct_table)
39 + return NULL;
40 +
41 + return ppe->acct_table + index * sizeof(struct mtk_foe_accounting);
42 +}
43 +
44 +struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index)
45 {
46 u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high;
47 u32 val, cnt_r0, cnt_r1, cnt_r2;
48 + struct mtk_foe_accounting *acct;
49 int ret;
50
51 val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST;
52 ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val);
53
54 + acct = mtk_ppe_acct_data(ppe, index);
55 + if (!acct)
56 + return NULL;
57 +
58 ret = mtk_ppe_mib_wait_busy(ppe);
59 if (ret)
60 - return ret;
61 + return acct;
62
63 cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
64 cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
65 @@ -111,10 +125,11 @@ static int mtk_mib_entry_read(struct mtk
66 byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1);
67 pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1);
68 pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2);
69 - *bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
70 - *packets = (pkt_cnt_high << 16) | pkt_cnt_low;
71
72 - return 0;
73 + acct->bytes += ((u64)byte_cnt_high << 32) | byte_cnt_low;
74 + acct->packets += (pkt_cnt_high << 16) | pkt_cnt_low;
75 +
76 + return acct;
77 }
78
79 static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
80 @@ -508,13 +523,6 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
81 hwe->ib1 &= ~MTK_FOE_IB1_STATE;
82 hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID);
83 dma_wmb();
84 - if (ppe->accounting) {
85 - struct mtk_foe_accounting *acct;
86 -
87 - acct = ppe->acct_table + entry->hash * sizeof(*acct);
88 - acct->packets = 0;
89 - acct->bytes = 0;
90 - }
91 }
92 entry->hash = 0xffff;
93
94 @@ -539,11 +547,14 @@ static int __mtk_foe_entry_idle_time(str
95 }
96
97 static bool
98 -mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
99 +mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
100 + u64 *packets, u64 *bytes)
101 {
102 + struct mtk_foe_accounting *acct;
103 struct mtk_foe_entry foe = {};
104 struct mtk_foe_entry *hwe;
105 u16 hash = entry->hash;
106 + bool ret = false;
107 int len;
108
109 if (hash == 0xffff)
110 @@ -554,18 +565,35 @@ mtk_flow_entry_update(struct mtk_ppe *pp
111 memcpy(&foe, hwe, len);
112
113 if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) ||
114 - FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND)
115 - return false;
116 + FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) {
117 + acct = mtk_ppe_acct_data(ppe, hash);
118 + if (acct) {
119 + entry->prev_packets += acct->packets;
120 + entry->prev_bytes += acct->bytes;
121 + }
122 +
123 + goto out;
124 + }
125
126 entry->data.ib1 = foe.ib1;
127 + acct = mtk_ppe_mib_entry_read(ppe, hash);
128 + ret = true;
129 +
130 +out:
131 + if (acct) {
132 + *packets += acct->packets;
133 + *bytes += acct->bytes;
134 + }
135
136 - return true;
137 + return ret;
138 }
139
140 static void
141 mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
142 {
143 u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
144 + u64 *packets = &entry->packets;
145 + u64 *bytes = &entry->bytes;
146 struct mtk_flow_entry *cur;
147 struct hlist_node *tmp;
148 int idle;
149 @@ -574,7 +602,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe
150 hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) {
151 int cur_idle;
152
153 - if (!mtk_flow_entry_update(ppe, cur)) {
154 + if (!mtk_flow_entry_update(ppe, cur, packets, bytes)) {
155 + entry->prev_packets += cur->prev_packets;
156 + entry->prev_bytes += cur->prev_bytes;
157 __mtk_foe_entry_clear(ppe, entry, false);
158 continue;
159 }
160 @@ -589,10 +619,29 @@ mtk_flow_entry_update_l2(struct mtk_ppe
161 }
162 }
163
164 +void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
165 + int *idle)
166 +{
167 + entry->packets = entry->prev_packets;
168 + entry->bytes = entry->prev_bytes;
169 +
170 + spin_lock_bh(&ppe_lock);
171 +
172 + if (entry->type == MTK_FLOW_TYPE_L2)
173 + mtk_flow_entry_update_l2(ppe, entry);
174 + else
175 + mtk_flow_entry_update(ppe, entry, &entry->packets, &entry->bytes);
176 +
177 + *idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
178 +
179 + spin_unlock_bh(&ppe_lock);
180 +}
181 +
182 static void
183 __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
184 u16 hash)
185 {
186 + struct mtk_foe_accounting *acct;
187 struct mtk_eth *eth = ppe->eth;
188 u16 timestamp = mtk_eth_timestamp(eth);
189 struct mtk_foe_entry *hwe;
190 @@ -617,6 +666,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
191
192 dma_wmb();
193
194 + acct = mtk_ppe_mib_entry_read(ppe, hash);
195 + if (acct) {
196 + acct->packets = 0;
197 + acct->bytes = 0;
198 + }
199 +
200 mtk_ppe_cache_clear(ppe);
201 }
202
203 @@ -781,21 +836,6 @@ out:
204 spin_unlock_bh(&ppe_lock);
205 }
206
207 -int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
208 -{
209 - int idle;
210 -
211 - spin_lock_bh(&ppe_lock);
212 - if (entry->type == MTK_FLOW_TYPE_L2)
213 - mtk_flow_entry_update_l2(ppe, entry);
214 - else
215 - mtk_flow_entry_update(ppe, entry);
216 - idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
217 - spin_unlock_bh(&ppe_lock);
218 -
219 - return idle;
220 -}
221 -
222 int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
223 {
224 if (!ppe)
225 @@ -823,32 +863,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe
226 return mtk_ppe_wait_busy(ppe);
227 }
228
229 -struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
230 - struct mtk_foe_accounting *diff)
231 -{
232 - struct mtk_foe_accounting *acct;
233 - int size = sizeof(struct mtk_foe_accounting);
234 - u64 bytes, packets;
235 -
236 - if (!ppe->accounting)
237 - return NULL;
238 -
239 - if (mtk_mib_entry_read(ppe, index, &bytes, &packets))
240 - return NULL;
241 -
242 - acct = ppe->acct_table + index * size;
243 -
244 - acct->bytes += bytes;
245 - acct->packets += packets;
246 -
247 - if (diff) {
248 - diff->bytes = bytes;
249 - diff->packets = packets;
250 - }
251 -
252 - return acct;
253 -}
254 -
255 struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
256 {
257 bool accounting = eth->soc->has_accounting;
258 --- a/drivers/net/ethernet/mediatek/mtk_ppe.h
259 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
260 @@ -283,6 +283,8 @@ struct mtk_flow_entry {
261 struct mtk_foe_entry data;
262 struct rhash_head node;
263 unsigned long cookie;
264 + u64 prev_packets, prev_bytes;
265 + u64 packets, bytes;
266 };
267
268 struct mtk_mib_entry {
269 @@ -325,6 +327,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
270 void mtk_ppe_start(struct mtk_ppe *ppe);
271 int mtk_ppe_stop(struct mtk_ppe *ppe);
272 int mtk_ppe_prepare_reset(struct mtk_ppe *ppe);
273 +struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index);
274
275 void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
276
277 @@ -373,9 +376,8 @@ int mtk_foe_entry_set_queue(struct mtk_e
278 unsigned int queue);
279 int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
280 void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
281 -int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
282 int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index);
283 -struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
284 - struct mtk_foe_accounting *diff);
285 +void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
286 + int *idle);
287
288 #endif
289 --- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
290 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
291 @@ -96,7 +96,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file
292 if (bind && state != MTK_FOE_STATE_BIND)
293 continue;
294
295 - acct = mtk_foe_entry_get_mib(ppe, i, NULL);
296 + acct = mtk_ppe_mib_entry_read(ppe, i);
297
298 type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
299 seq_printf(m, "%05x %s %7s", i,
300 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
301 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
302 @@ -499,24 +499,21 @@ static int
303 mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
304 {
305 struct mtk_flow_entry *entry;
306 - struct mtk_foe_accounting diff;
307 - u32 idle;
308 + u64 packets, bytes;
309 + int idle;
310
311 entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
312 mtk_flow_ht_params);
313 if (!entry)
314 return -ENOENT;
315
316 - idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
317 + packets = entry->packets;
318 + bytes = entry->bytes;
319 + mtk_foe_entry_get_stats(eth->ppe[entry->ppe_index], entry, &idle);
320 + f->stats.pkts += entry->packets - packets;
321 + f->stats.bytes += entry->bytes - bytes;
322 f->stats.lastused = jiffies - idle * HZ;
323
324 - if (entry->hash != 0xFFFF &&
325 - mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash,
326 - &diff)) {
327 - f->stats.pkts += diff.packets;
328 - f->stats.bytes += diff.bytes;
329 - }
330 -
331 return 0;
332 }
333