blob: 5d821f0d749e6d6f43bf7c5ec8f78cd4f461a8bd [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001From: Felix Fietkau <nbd@nbd.name>
2Date: Sun, 11 Oct 2020 22:28:32 +0200
3Subject: [PATCH] net: ethernet: mediatek: mtk_eth_soc: add flow offloading
4 support
5
6Only supports IPv4 for now
7
8Signed-off-by: Felix Fietkau <nbd@nbd.name>
9---
10 create mode 100644 drivers/net/ethernet/mediatek/mtk_offload.c
11 create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
12
13--- a/drivers/net/ethernet/mediatek/Makefile
14+++ b/drivers/net/ethernet/mediatek/Makefile
15@@ -4,4 +4,4 @@
16 #
17
18 obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
19-mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o
20+mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_offload.o
21--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
22+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
23@@ -20,6 +20,8 @@
24 #include <linux/pinctrl/devinfo.h>
25 #include <linux/phylink.h>
26 #include <linux/jhash.h>
27+#include <linux/netfilter.h>
28+#include <net/netfilter/nf_flow_table.h>
29 #include <net/dsa.h>
30
31 #include "mtk_eth_soc.h"
32@@ -1362,8 +1364,12 @@ static int mtk_poll_rx(struct napi_struc
33 (trxd.rxd2 & RX_DMA_VTAG))
34 __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
35 RX_DMA_VID(trxd.rxd3));
36- skb_record_rx_queue(skb, 0);
37- napi_gro_receive(napi, skb);
38+ if (mtk_offload_check_rx(eth, skb, trxd.rxd4) == 0) {
39+ skb_record_rx_queue(skb, 0);
40+ napi_gro_receive(napi, skb);
41+ } else {
42+ dev_kfree_skb(skb);
43+ }
44
45 skip_rx:
46 ring->data[idx] = new_data;
47@@ -2902,6 +2908,25 @@ static int mtk_set_rxnfc(struct net_devi
48 return ret;
49 }
50
51+static int
52+mtk_flow_offload(enum flow_offload_type type, struct flow_offload *flow,
53+ struct flow_offload_hw_path *src,
54+ struct flow_offload_hw_path *dest)
55+{
56+ struct mtk_mac *mac = netdev_priv(src->dev);
57+ struct mtk_eth *eth = mac->hw;
58+
59+ if (!eth->soc->offload_version)
60+ return -EINVAL;
61+
62+ if (src->dev->base_addr != dest->dev->base_addr)
63+ return -EINVAL;
64+
65+ mac = netdev_priv(src->dev);
66+
67+ return mtk_flow_offload_add(eth, type, flow, src, dest);
68+}
69+
70 static const struct ethtool_ops mtk_ethtool_ops = {
71 .get_link_ksettings = mtk_get_link_ksettings,
72 .set_link_ksettings = mtk_set_link_ksettings,
73@@ -2933,6 +2958,7 @@ static const struct net_device_ops mtk_n
74 #ifdef CONFIG_NET_POLL_CONTROLLER
75 .ndo_poll_controller = mtk_poll_controller,
76 #endif
77+ .ndo_flow_offload = mtk_flow_offload,
78 };
79
80 static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
81@@ -3198,6 +3224,10 @@ static int mtk_probe(struct platform_dev
82 eth->base + MTK_ETH_PPE_BASE, 2);
83 if (err)
84 goto err_free_dev;
85+
86+ err = mtk_flow_offload_init(eth);
87+ if (err)
88+ goto err_free_dev;
89 }
90
91 for (i = 0; i < MTK_MAX_DEVS; i++) {
92--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
93+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
94@@ -949,6 +949,7 @@ struct mtk_eth {
95 int ip_align;
96
97 struct mtk_ppe ppe;
98+ struct flow_offload __rcu **foe_flow_table;
99 };
100
101 /* struct mtk_mac - the structure that holds the info about the MACs of the
102@@ -993,4 +994,12 @@ int mtk_gmac_sgmii_path_setup(struct mtk
103 int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
104 int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
105
106+int mtk_flow_offload_init(struct mtk_eth *eth);
107+int mtk_flow_offload_add(struct mtk_eth *eth,
108+ enum flow_offload_type type,
109+ struct flow_offload *flow,
110+ struct flow_offload_hw_path *src,
111+ struct flow_offload_hw_path *dest);
112+int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4);
113+
114 #endif /* MTK_ETH_H */
115--- /dev/null
116+++ b/drivers/net/ethernet/mediatek/mtk_offload.c
117@@ -0,0 +1,146 @@
118+/* This program is free software; you can redistribute it and/or modify
119+ * it under the terms of the GNU General Public License as published by
120+ * the Free Software Foundation; version 2 of the License
121+ *
122+ * This program is distributed in the hope that it will be useful,
123+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
124+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
125+ * GNU General Public License for more details.
126+ *
127+ * Copyright (C) 2018 John Crispin <john@phrozen.org>
128+ */
129+
130+#include <net/netfilter/nf_flow_table.h>
131+#include "mtk_eth_soc.h"
132+
133+static int
134+mtk_offload_prepare_v4(struct mtk_eth *eth, struct mtk_foe_entry *entry,
135+ struct flow_offload_tuple *s_tuple,
136+ struct flow_offload_tuple *d_tuple,
137+ struct flow_offload_hw_path *src,
138+ struct flow_offload_hw_path *dest)
139+{
140+ int dest_port = 1;
141+
142+ if (dest->dev == eth->netdev[1])
143+ dest_port = 2;
144+
145+ mtk_foe_entry_prepare(entry, MTK_PPE_PKT_TYPE_IPV4_HNAPT, s_tuple->l4proto,
146+ dest_port, dest->eth_src, dest->eth_dest);
147+ mtk_foe_entry_set_ipv4_tuple(entry, false,
148+ s_tuple->src_v4.s_addr, s_tuple->src_port,
149+ s_tuple->dst_v4.s_addr, s_tuple->dst_port);
150+ mtk_foe_entry_set_ipv4_tuple(entry, true,
151+ d_tuple->dst_v4.s_addr, d_tuple->dst_port,
152+ d_tuple->src_v4.s_addr, d_tuple->src_port);
153+
154+ if (dest->flags & FLOW_OFFLOAD_PATH_PPPOE)
155+ mtk_foe_entry_set_pppoe(entry, dest->pppoe_sid);
156+
157+ if (dest->flags & FLOW_OFFLOAD_PATH_VLAN)
158+ mtk_foe_entry_set_vlan(entry, dest->vlan_id);
159+
160+ if (dest->flags & FLOW_OFFLOAD_PATH_DSA)
161+ mtk_foe_entry_set_dsa(entry, dest->dsa_port);
162+
163+ return 0;
164+}
165+
166+int mtk_flow_offload_add(struct mtk_eth *eth,
167+ enum flow_offload_type type,
168+ struct flow_offload *flow,
169+ struct flow_offload_hw_path *src,
170+ struct flow_offload_hw_path *dest)
171+{
172+ struct flow_offload_tuple *otuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple;
173+ struct flow_offload_tuple *rtuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple;
174+ struct mtk_foe_entry orig, reply;
175+ u32 ohash, rhash, timestamp;
176+
177+ if (otuple->l4proto != IPPROTO_TCP && otuple->l4proto != IPPROTO_UDP)
178+ return -EINVAL;
179+
180+ if (type == FLOW_OFFLOAD_DEL) {
181+ ohash = (unsigned long)flow->priv;
182+ rhash = ohash >> 16;
183+ ohash &= 0xffff;
184+ mtk_foe_entry_clear(&eth->ppe, ohash);
185+ mtk_foe_entry_clear(&eth->ppe, rhash);
186+ rcu_assign_pointer(eth->foe_flow_table[ohash], NULL);
187+ rcu_assign_pointer(eth->foe_flow_table[rhash], NULL);
188+ synchronize_rcu();
189+
190+ return 0;
191+ }
192+
193+ switch (otuple->l3proto) {
194+ case AF_INET:
195+ if (mtk_offload_prepare_v4(eth, &orig, otuple, rtuple, src, dest) ||
196+ mtk_offload_prepare_v4(eth, &reply, rtuple, otuple, dest, src))
197+ return -EINVAL;
198+ break;
199+ default:
200+ return -EINVAL;
201+ }
202+
203+ timestamp = mtk_r32(eth, 0x0010);
204+
205+ ohash = mtk_foe_entry_commit(&eth->ppe, &orig, timestamp);
206+ if (ohash < 0)
207+ return -EINVAL;
208+
209+ rhash = mtk_foe_entry_commit(&eth->ppe, &reply, timestamp);
210+ if (rhash < 0) {
211+ mtk_foe_entry_clear(&eth->ppe, ohash);
212+ return -EINVAL;
213+ }
214+
215+ rcu_assign_pointer(eth->foe_flow_table[ohash], flow);
216+ rcu_assign_pointer(eth->foe_flow_table[rhash], flow);
217+
218+ ohash |= rhash << 16;
219+ flow->priv = (void *)(unsigned long)ohash;
220+
221+ return 0;
222+}
223+
224+static void mtk_offload_keepalive(struct mtk_eth *eth, unsigned int hash)
225+{
226+ struct flow_offload *flow;
227+
228+ rcu_read_lock();
229+ flow = rcu_dereference(eth->foe_flow_table[hash]);
230+ if (flow)
231+ flow->timeout = jiffies + 30 * HZ;
232+ rcu_read_unlock();
233+}
234+
235+int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4)
236+{
237+ unsigned int hash;
238+
239+ switch (FIELD_GET(MTK_RXD4_PPE_CPU_REASON, rxd4)) {
240+ case MTK_PPE_CPU_REASON_KEEPALIVE_UC_OLD_HDR:
241+ case MTK_PPE_CPU_REASON_KEEPALIVE_MC_NEW_HDR:
242+ case MTK_PPE_CPU_REASON_KEEPALIVE_DUP_OLD_HDR:
243+ hash = FIELD_GET(MTK_RXD4_FOE_ENTRY, rxd4);
244+ mtk_offload_keepalive(eth, hash);
245+ return -1;
246+ case MTK_PPE_CPU_REASON_PACKET_SAMPLING:
247+ return -1;
248+ default:
249+ return 0;
250+ }
251+}
252+
253+int mtk_flow_offload_init(struct mtk_eth *eth)
254+{
255+ eth->foe_flow_table = devm_kcalloc(eth->dev, MTK_PPE_ENTRIES,
256+ sizeof(*eth->foe_flow_table),
257+ GFP_KERNEL);
258+
259+ if (!eth->foe_flow_table)
260+ return -ENOMEM;
261+
262+ return 0;
263+}
264--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
265+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
266@@ -375,6 +375,8 @@ int mtk_ppe_init(struct mtk_ppe *ppe, st
267
268 ppe->foe_table = foe;
269
270+ mtk_ppe_debugfs_init(ppe);
271+
272 return 0;
273 }
274
275--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
276+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
277@@ -271,4 +271,7 @@ int mtk_foe_entry_set_pppoe(struct mtk_f
278 int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
279 u16 timestamp);
280
281+/* internal */
282+int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
283+
284 #endif
285--- /dev/null
286+++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
287@@ -0,0 +1,114 @@
288+/* This program is free software; you can redistribute it and/or modify
289+ * it under the terms of the GNU General Public License as published by
290+ * the Free Software Foundation; version 2 of the License
291+ *
292+ * This program is distributed in the hope that it will be useful,
293+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
294+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
295+ * GNU General Public License for more details.
296+ *
297+ * Copyright (C) 2014-2016 Sean Wang <sean.wang@mediatek.com>
298+ * Copyright (C) 2016-2017 John Crispin <blogic@openwrt.org>
299+ * Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
300+ */
301+
302+#include <linux/kernel.h>
303+#include <linux/debugfs.h>
304+#include "mtk_eth_soc.h"
305+
306+static const char *mtk_foe_entry_state_str[] = {
307+ "INVALID",
308+ "UNBIND",
309+ "BIND",
310+ "FIN"
311+};
312+
313+static const char *mtk_foe_packet_type_str[] = {
314+ "IPV4_HNAPT",
315+ "IPV4_HNAT",
316+ "IPV6_1T_ROUTE",
317+ "IPV4_DSLITE",
318+ "IPV6_3T_ROUTE",
319+ "IPV6_5T_ROUTE",
320+ "IPV6_6RD",
321+};
322+
323+#define es(entry) (mtk_foe_entry_state_str[FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1)])
324+//#define ei(entry, end) (MTK_PPE_TBL_SZ - (int)(end - entry))
325+#define pt(entry) (mtk_foe_packet_type_str[FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1)])
326+
327+static int mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private)
328+{
329+ struct mtk_ppe *ppe = m->private;
330+ int i, count;
331+
332+ for (i = 0, count = 0; i < MTK_PPE_ENTRIES; i++) {
333+ struct mtk_foe_entry *entry = &ppe->foe_table[i];
334+
335+ if (!FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1))
336+ continue;
337+
338+ if (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1) ==
339+ MTK_PPE_PKT_TYPE_IPV4_HNAPT) {
340+ struct mtk_foe_ipv4 *ip4 = &entry->ipv4;
341+ struct mtk_foe_mac_info *l2 = &ip4->l2;
342+
343+ __be32 saddr = htonl(ip4->orig.src_ip);
344+ __be32 daddr = htonl(ip4->orig.dest_ip);
345+ __be32 nsaddr = htonl(ip4->new.src_ip);
346+ __be32 ndaddr = htonl(ip4->new.dest_ip);
347+ unsigned char h_dest[ETH_ALEN];
348+ unsigned char h_source[ETH_ALEN];
349+
350+ *((__be32 *) h_source) = htonl(l2->src_mac_hi);
351+ *((__be16*) &h_source[4]) = htons(l2->src_mac_lo);
352+ *((__be32*) h_dest) = htonl(l2->dest_mac_hi);
353+ *((__be16*) &h_dest[4]) = htons(l2->dest_mac_lo);
354+ seq_printf(m,
355+ "(%x)0x%05x|state=%s|type=%s|"
356+ "%pI4:%d->%pI4:%d=>%pI4:%d->%pI4:%d|%pM=>%pM|"
357+ "etype=0x%04x|info1=0x%x|info2=0x%x|"
358+ "vlan1=%d|vlan2=%d\n",
359+ count, i, es(entry), pt(entry),
360+ &saddr, ip4->orig.src_port,
361+ &daddr, ip4->orig.dest_port,
362+ &nsaddr, ip4->new.src_port,
363+ &ndaddr, ip4->new.dest_port,
364+ h_source, h_dest,
365+ ntohs(l2->etype),
366+ entry->ib1,
367+ ip4->ib2,
368+ l2->vlan1,
369+ l2->vlan2);
370+ count++;
371+ } else
372+ seq_printf(m, "0x%05x state=%s\n", count, es(entry));
373+ }
374+
375+ return 0;
376+}
377+
378+static int mtk_ppe_debugfs_foe_open(struct inode *inode, struct file *file)
379+{
380+ return single_open(file, mtk_ppe_debugfs_foe_show, inode->i_private);
381+}
382+
383+static const struct file_operations mtk_ppe_debugfs_foe_fops = {
384+ .open = mtk_ppe_debugfs_foe_open,
385+ .read = seq_read,
386+ .llseek = seq_lseek,
387+ .release = single_release,
388+};
389+
390+int mtk_ppe_debugfs_init(struct mtk_ppe *ppe)
391+{
392+ struct dentry *root;
393+
394+ root = debugfs_create_dir("mtk_ppe", NULL);
395+ if (!root)
396+ return -ENOMEM;
397+
398+ debugfs_create_file("entries", S_IRUGO, root, ppe, &mtk_ppe_debugfs_foe_fops);
399+
400+ return 0;
401+}