1 From: Felix Fietkau <nbd@nbd.name>
2 Date: Sun, 11 Oct 2020 22:28:32 +0200
3 Subject: [PATCH] net: ethernet: mediatek: mtk_eth_soc: add flow offloading
6 Only supports IPv4 for now
8 Signed-off-by: Felix Fietkau <nbd@nbd.name>
10 create mode 100644 drivers/net/ethernet/mediatek/mtk_offload.c
11 create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
13 --- a/drivers/net/ethernet/mediatek/Makefile
14 +++ b/drivers/net/ethernet/mediatek/Makefile
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
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>
31 #include "mtk_eth_soc.h"
32 @@ -1348,8 +1350,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);
46 ring->data[idx] = new_data;
47 @@ -2882,6 +2888,25 @@ static int mtk_set_rxnfc(struct net_devi
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)
56 + struct mtk_mac *mac = netdev_priv(src->dev);
57 + struct mtk_eth *eth = mac->hw;
59 + if (!eth->soc->offload_version)
62 + if (src->dev->base_addr != dest->dev->base_addr)
65 + mac = netdev_priv(src->dev);
67 + return mtk_flow_offload_add(eth, type, flow, src, dest);
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 @@ -2913,6 +2938,7 @@ static const struct net_device_ops mtk_n
74 #ifdef CONFIG_NET_POLL_CONTROLLER
75 .ndo_poll_controller = mtk_poll_controller,
77 + .ndo_flow_offload = mtk_flow_offload,
80 static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
81 @@ -3178,6 +3204,10 @@ static int mtk_probe(struct platform_dev
82 eth->base + MTK_ETH_PPE_BASE, 2);
86 + err = mtk_flow_offload_init(eth);
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 {
98 + struct flow_offload __rcu **foe_flow_table;
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);
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);
114 #endif /* MTK_ETH_H */
116 +++ b/drivers/net/ethernet/mediatek/mtk_offload.c
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
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.
127 + * Copyright (C) 2018 John Crispin <john@phrozen.org>
130 +#include <net/netfilter/nf_flow_table.h>
131 +#include "mtk_eth_soc.h"
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)
142 + if (dest->dev == eth->netdev[1])
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);
154 + if (dest->flags & FLOW_OFFLOAD_PATH_PPPOE)
155 + mtk_foe_entry_set_pppoe(entry, dest->pppoe_sid);
157 + if (dest->flags & FLOW_OFFLOAD_PATH_VLAN)
158 + mtk_foe_entry_set_vlan(entry, dest->vlan_id);
160 + if (dest->flags & FLOW_OFFLOAD_PATH_DSA)
161 + mtk_foe_entry_set_dsa(entry, dest->dsa_port);
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)
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;
177 + if (otuple->l4proto != IPPROTO_TCP && otuple->l4proto != IPPROTO_UDP)
180 + if (type == FLOW_OFFLOAD_DEL) {
181 + ohash = (unsigned long)flow->priv;
182 + rhash = ohash >> 16;
184 + mtk_foe_entry_clear(ð->ppe, ohash);
185 + mtk_foe_entry_clear(ð->ppe, rhash);
186 + rcu_assign_pointer(eth->foe_flow_table[ohash], NULL);
187 + rcu_assign_pointer(eth->foe_flow_table[rhash], NULL);
193 + switch (otuple->l3proto) {
195 + if (mtk_offload_prepare_v4(eth, &orig, otuple, rtuple, src, dest) ||
196 + mtk_offload_prepare_v4(eth, &reply, rtuple, otuple, dest, src))
203 + timestamp = mtk_r32(eth, 0x0010);
205 + ohash = mtk_foe_entry_commit(ð->ppe, &orig, timestamp);
209 + rhash = mtk_foe_entry_commit(ð->ppe, &reply, timestamp);
211 + mtk_foe_entry_clear(ð->ppe, ohash);
215 + rcu_assign_pointer(eth->foe_flow_table[ohash], flow);
216 + rcu_assign_pointer(eth->foe_flow_table[rhash], flow);
218 + ohash |= rhash << 16;
219 + flow->priv = (void *)(unsigned long)ohash;
224 +static void mtk_offload_keepalive(struct mtk_eth *eth, unsigned int hash)
226 + struct flow_offload *flow;
229 + flow = rcu_dereference(eth->foe_flow_table[hash]);
231 + flow->timeout = jiffies + 30 * HZ;
235 +int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4)
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);
246 + case MTK_PPE_CPU_REASON_PACKET_SAMPLING:
253 +int mtk_flow_offload_init(struct mtk_eth *eth)
255 + eth->foe_flow_table = devm_kcalloc(eth->dev, MTK_PPE_ENTRIES,
256 + sizeof(*eth->foe_flow_table),
259 + if (!eth->foe_flow_table)
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
268 ppe->foe_table = foe;
270 + mtk_ppe_debugfs_init(ppe);
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,
282 +int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
286 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
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
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.
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>
302 +#include <linux/kernel.h>
303 +#include <linux/debugfs.h>
304 +#include "mtk_eth_soc.h"
306 +static const char *mtk_foe_entry_state_str[] = {
313 +static const char *mtk_foe_packet_type_str[] = {
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)])
327 +static int mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private)
329 + struct mtk_ppe *ppe = m->private;
332 + for (i = 0, count = 0; i < MTK_PPE_ENTRIES; i++) {
333 + struct mtk_foe_entry *entry = &ppe->foe_table[i];
335 + if (!FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1))
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;
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];
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);
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,
372 + seq_printf(m, "0x%05x state=%s\n", count, es(entry));
378 +static int mtk_ppe_debugfs_foe_open(struct inode *inode, struct file *file)
380 + return single_open(file, mtk_ppe_debugfs_foe_show, inode->i_private);
383 +static const struct file_operations mtk_ppe_debugfs_foe_fops = {
384 + .open = mtk_ppe_debugfs_foe_open,
386 + .llseek = seq_lseek,
387 + .release = single_release,
390 +int mtk_ppe_debugfs_init(struct mtk_ppe *ppe)
392 + struct dentry *root;
394 + root = debugfs_create_dir("mtk_ppe", NULL);
398 + debugfs_create_file("entries", S_IRUGO, root, ppe, &mtk_ppe_debugfs_foe_fops);