xref: /openbmc/linux/drivers/net/ethernet/mediatek/mtk_ppe_offload.c (revision 5804c19b80bf625c6a9925317f845e497434d6d3)
1502e84e2SFelix Fietkau // SPDX-License-Identifier: GPL-2.0-only
2502e84e2SFelix Fietkau /*
3502e84e2SFelix Fietkau  *  Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
4502e84e2SFelix Fietkau  */
5502e84e2SFelix Fietkau 
6502e84e2SFelix Fietkau #include <linux/if_ether.h>
7502e84e2SFelix Fietkau #include <linux/rhashtable.h>
8502e84e2SFelix Fietkau #include <linux/ip.h>
9817b2fdfSDavid Bentham #include <linux/ipv6.h>
10502e84e2SFelix Fietkau #include <net/flow_offload.h>
11502e84e2SFelix Fietkau #include <net/pkt_cls.h>
12502e84e2SFelix Fietkau #include <net/dsa.h>
13502e84e2SFelix Fietkau #include "mtk_eth_soc.h"
14a333215eSFelix Fietkau #include "mtk_wed.h"
15502e84e2SFelix Fietkau 
16502e84e2SFelix Fietkau struct mtk_flow_data {
17502e84e2SFelix Fietkau 	struct ethhdr eth;
18502e84e2SFelix Fietkau 
19502e84e2SFelix Fietkau 	union {
20502e84e2SFelix Fietkau 		struct {
21502e84e2SFelix Fietkau 			__be32 src_addr;
22502e84e2SFelix Fietkau 			__be32 dst_addr;
23502e84e2SFelix Fietkau 		} v4;
24817b2fdfSDavid Bentham 
25817b2fdfSDavid Bentham 		struct {
26817b2fdfSDavid Bentham 			struct in6_addr src_addr;
27817b2fdfSDavid Bentham 			struct in6_addr dst_addr;
28817b2fdfSDavid Bentham 		} v6;
29502e84e2SFelix Fietkau 	};
30502e84e2SFelix Fietkau 
31502e84e2SFelix Fietkau 	__be16 src_port;
32502e84e2SFelix Fietkau 	__be16 dst_port;
33502e84e2SFelix Fietkau 
3433fc42deSFelix Fietkau 	u16 vlan_in;
3533fc42deSFelix Fietkau 
36502e84e2SFelix Fietkau 	struct {
37502e84e2SFelix Fietkau 		u16 id;
38502e84e2SFelix Fietkau 		__be16 proto;
39502e84e2SFelix Fietkau 		u8 num;
40502e84e2SFelix Fietkau 	} vlan;
41502e84e2SFelix Fietkau 	struct {
42502e84e2SFelix Fietkau 		u16 sid;
43502e84e2SFelix Fietkau 		u8 num;
44502e84e2SFelix Fietkau 	} pppoe;
45502e84e2SFelix Fietkau };
46502e84e2SFelix Fietkau 
47502e84e2SFelix Fietkau static const struct rhashtable_params mtk_flow_ht_params = {
48502e84e2SFelix Fietkau 	.head_offset = offsetof(struct mtk_flow_entry, node),
496ecaf81dSDENG Qingfang 	.key_offset = offsetof(struct mtk_flow_entry, cookie),
50502e84e2SFelix Fietkau 	.key_len = sizeof(unsigned long),
51502e84e2SFelix Fietkau 	.automatic_shrinking = true,
52502e84e2SFelix Fietkau };
53502e84e2SFelix Fietkau 
54502e84e2SFelix Fietkau static int
mtk_flow_set_ipv4_addr(struct mtk_eth * eth,struct mtk_foe_entry * foe,struct mtk_flow_data * data,bool egress)5503a3180eSLorenzo Bianconi mtk_flow_set_ipv4_addr(struct mtk_eth *eth, struct mtk_foe_entry *foe,
5603a3180eSLorenzo Bianconi 		       struct mtk_flow_data *data, bool egress)
57502e84e2SFelix Fietkau {
5803a3180eSLorenzo Bianconi 	return mtk_foe_entry_set_ipv4_tuple(eth, foe, egress,
59502e84e2SFelix Fietkau 					    data->v4.src_addr, data->src_port,
60502e84e2SFelix Fietkau 					    data->v4.dst_addr, data->dst_port);
61502e84e2SFelix Fietkau }
62502e84e2SFelix Fietkau 
63817b2fdfSDavid Bentham static int
mtk_flow_set_ipv6_addr(struct mtk_eth * eth,struct mtk_foe_entry * foe,struct mtk_flow_data * data)6403a3180eSLorenzo Bianconi mtk_flow_set_ipv6_addr(struct mtk_eth *eth, struct mtk_foe_entry *foe,
6503a3180eSLorenzo Bianconi 		       struct mtk_flow_data *data)
66817b2fdfSDavid Bentham {
6703a3180eSLorenzo Bianconi 	return mtk_foe_entry_set_ipv6_tuple(eth, foe,
68817b2fdfSDavid Bentham 					    data->v6.src_addr.s6_addr32, data->src_port,
69817b2fdfSDavid Bentham 					    data->v6.dst_addr.s6_addr32, data->dst_port);
70817b2fdfSDavid Bentham }
71817b2fdfSDavid Bentham 
72502e84e2SFelix Fietkau static void
mtk_flow_offload_mangle_eth(const struct flow_action_entry * act,void * eth)73502e84e2SFelix Fietkau mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
74502e84e2SFelix Fietkau {
75502e84e2SFelix Fietkau 	void *dest = eth + act->mangle.offset;
76502e84e2SFelix Fietkau 	const void *src = &act->mangle.val;
77502e84e2SFelix Fietkau 
78502e84e2SFelix Fietkau 	if (act->mangle.offset > 8)
79502e84e2SFelix Fietkau 		return;
80502e84e2SFelix Fietkau 
81502e84e2SFelix Fietkau 	if (act->mangle.mask == 0xffff) {
82502e84e2SFelix Fietkau 		src += 2;
83502e84e2SFelix Fietkau 		dest += 2;
84502e84e2SFelix Fietkau 	}
85502e84e2SFelix Fietkau 
86502e84e2SFelix Fietkau 	memcpy(dest, src, act->mangle.mask ? 2 : 4);
87502e84e2SFelix Fietkau }
88502e84e2SFelix Fietkau 
89a333215eSFelix Fietkau static int
mtk_flow_get_wdma_info(struct net_device * dev,const u8 * addr,struct mtk_wdma_info * info)90a333215eSFelix Fietkau mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info)
91a333215eSFelix Fietkau {
922830e314SLorenzo Bianconi 	struct net_device_path_stack stack;
932830e314SLorenzo Bianconi 	struct net_device_path *path;
942830e314SLorenzo Bianconi 	int err;
95a333215eSFelix Fietkau 
962830e314SLorenzo Bianconi 	if (!dev)
9753eb9b04SLorenzo Bianconi 		return -ENODEV;
9853eb9b04SLorenzo Bianconi 
99a333215eSFelix Fietkau 	if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED))
100a333215eSFelix Fietkau 		return -1;
101a333215eSFelix Fietkau 
1022830e314SLorenzo Bianconi 	err = dev_fill_forward_path(dev, addr, &stack);
1032830e314SLorenzo Bianconi 	if (err)
1042830e314SLorenzo Bianconi 		return err;
1052830e314SLorenzo Bianconi 
1062830e314SLorenzo Bianconi 	path = &stack.path[stack.num_paths - 1];
1072830e314SLorenzo Bianconi 	if (path->type != DEV_PATH_MTK_WDMA)
108a333215eSFelix Fietkau 		return -1;
109a333215eSFelix Fietkau 
1102830e314SLorenzo Bianconi 	info->wdma_idx = path->mtk_wdma.wdma_idx;
1112830e314SLorenzo Bianconi 	info->queue = path->mtk_wdma.queue;
1122830e314SLorenzo Bianconi 	info->bss = path->mtk_wdma.bss;
1132830e314SLorenzo Bianconi 	info->wcid = path->mtk_wdma.wcid;
114a333215eSFelix Fietkau 
115a333215eSFelix Fietkau 	return 0;
116a333215eSFelix Fietkau }
117a333215eSFelix Fietkau 
118502e84e2SFelix Fietkau 
119502e84e2SFelix Fietkau static int
mtk_flow_mangle_ports(const struct flow_action_entry * act,struct mtk_flow_data * data)120502e84e2SFelix Fietkau mtk_flow_mangle_ports(const struct flow_action_entry *act,
121502e84e2SFelix Fietkau 		      struct mtk_flow_data *data)
122502e84e2SFelix Fietkau {
123502e84e2SFelix Fietkau 	u32 val = ntohl(act->mangle.val);
124502e84e2SFelix Fietkau 
125502e84e2SFelix Fietkau 	switch (act->mangle.offset) {
126502e84e2SFelix Fietkau 	case 0:
127502e84e2SFelix Fietkau 		if (act->mangle.mask == ~htonl(0xffff))
128502e84e2SFelix Fietkau 			data->dst_port = cpu_to_be16(val);
129502e84e2SFelix Fietkau 		else
130502e84e2SFelix Fietkau 			data->src_port = cpu_to_be16(val >> 16);
131502e84e2SFelix Fietkau 		break;
132502e84e2SFelix Fietkau 	case 2:
133502e84e2SFelix Fietkau 		data->dst_port = cpu_to_be16(val);
134502e84e2SFelix Fietkau 		break;
135502e84e2SFelix Fietkau 	default:
136502e84e2SFelix Fietkau 		return -EINVAL;
137502e84e2SFelix Fietkau 	}
138502e84e2SFelix Fietkau 
139502e84e2SFelix Fietkau 	return 0;
140502e84e2SFelix Fietkau }
141502e84e2SFelix Fietkau 
142502e84e2SFelix Fietkau static int
mtk_flow_mangle_ipv4(const struct flow_action_entry * act,struct mtk_flow_data * data)143502e84e2SFelix Fietkau mtk_flow_mangle_ipv4(const struct flow_action_entry *act,
144502e84e2SFelix Fietkau 		     struct mtk_flow_data *data)
145502e84e2SFelix Fietkau {
146502e84e2SFelix Fietkau 	__be32 *dest;
147502e84e2SFelix Fietkau 
148502e84e2SFelix Fietkau 	switch (act->mangle.offset) {
149502e84e2SFelix Fietkau 	case offsetof(struct iphdr, saddr):
150502e84e2SFelix Fietkau 		dest = &data->v4.src_addr;
151502e84e2SFelix Fietkau 		break;
152502e84e2SFelix Fietkau 	case offsetof(struct iphdr, daddr):
153502e84e2SFelix Fietkau 		dest = &data->v4.dst_addr;
154502e84e2SFelix Fietkau 		break;
155502e84e2SFelix Fietkau 	default:
156502e84e2SFelix Fietkau 		return -EINVAL;
157502e84e2SFelix Fietkau 	}
158502e84e2SFelix Fietkau 
159502e84e2SFelix Fietkau 	memcpy(dest, &act->mangle.val, sizeof(u32));
160502e84e2SFelix Fietkau 
161502e84e2SFelix Fietkau 	return 0;
162502e84e2SFelix Fietkau }
163502e84e2SFelix Fietkau 
164502e84e2SFelix Fietkau static int
mtk_flow_get_dsa_port(struct net_device ** dev)165502e84e2SFelix Fietkau mtk_flow_get_dsa_port(struct net_device **dev)
166502e84e2SFelix Fietkau {
167502e84e2SFelix Fietkau #if IS_ENABLED(CONFIG_NET_DSA)
168502e84e2SFelix Fietkau 	struct dsa_port *dp;
169502e84e2SFelix Fietkau 
170502e84e2SFelix Fietkau 	dp = dsa_port_from_netdev(*dev);
171502e84e2SFelix Fietkau 	if (IS_ERR(dp))
172502e84e2SFelix Fietkau 		return -ENODEV;
173502e84e2SFelix Fietkau 
174502e84e2SFelix Fietkau 	if (dp->cpu_dp->tag_ops->proto != DSA_TAG_PROTO_MTK)
175502e84e2SFelix Fietkau 		return -ENODEV;
176502e84e2SFelix Fietkau 
1778f6a19c0SVladimir Oltean 	*dev = dsa_port_to_master(dp);
178502e84e2SFelix Fietkau 
179502e84e2SFelix Fietkau 	return dp->index;
180502e84e2SFelix Fietkau #else
181502e84e2SFelix Fietkau 	return -ENODEV;
182502e84e2SFelix Fietkau #endif
183502e84e2SFelix Fietkau }
184502e84e2SFelix Fietkau 
185502e84e2SFelix Fietkau static int
mtk_flow_set_output_device(struct mtk_eth * eth,struct mtk_foe_entry * foe,struct net_device * dev,const u8 * dest_mac,int * wed_index)186502e84e2SFelix Fietkau mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
187a333215eSFelix Fietkau 			   struct net_device *dev, const u8 *dest_mac,
188a333215eSFelix Fietkau 			   int *wed_index)
189502e84e2SFelix Fietkau {
190a333215eSFelix Fietkau 	struct mtk_wdma_info info = {};
1918bd8dcc5SFelix Fietkau 	int pse_port, dsa_port, queue;
192502e84e2SFelix Fietkau 
193a333215eSFelix Fietkau 	if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
19403a3180eSLorenzo Bianconi 		mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue,
19503a3180eSLorenzo Bianconi 				       info.bss, info.wcid);
196a008e2a8SLorenzo Bianconi 		if (mtk_is_netsys_v2_or_greater(eth)) {
19703a3180eSLorenzo Bianconi 			switch (info.wdma_idx) {
19803a3180eSLorenzo Bianconi 			case 0:
19903a3180eSLorenzo Bianconi 				pse_port = 8;
20003a3180eSLorenzo Bianconi 				break;
20103a3180eSLorenzo Bianconi 			case 1:
20203a3180eSLorenzo Bianconi 				pse_port = 9;
20303a3180eSLorenzo Bianconi 				break;
20403a3180eSLorenzo Bianconi 			default:
20503a3180eSLorenzo Bianconi 				return -EINVAL;
20603a3180eSLorenzo Bianconi 			}
20703a3180eSLorenzo Bianconi 		} else {
208a333215eSFelix Fietkau 			pse_port = 3;
20903a3180eSLorenzo Bianconi 		}
210a333215eSFelix Fietkau 		*wed_index = info.wdma_idx;
211a333215eSFelix Fietkau 		goto out;
212a333215eSFelix Fietkau 	}
213a333215eSFelix Fietkau 
214502e84e2SFelix Fietkau 	dsa_port = mtk_flow_get_dsa_port(&dev);
215502e84e2SFelix Fietkau 
216502e84e2SFelix Fietkau 	if (dev == eth->netdev[0])
217*5a124b1fSLorenzo Bianconi 		pse_port = PSE_GDM1_PORT;
218502e84e2SFelix Fietkau 	else if (dev == eth->netdev[1])
219*5a124b1fSLorenzo Bianconi 		pse_port = PSE_GDM2_PORT;
220*5a124b1fSLorenzo Bianconi 	else if (dev == eth->netdev[2])
221*5a124b1fSLorenzo Bianconi 		pse_port = PSE_GDM3_PORT;
222502e84e2SFelix Fietkau 	else
223502e84e2SFelix Fietkau 		return -EOPNOTSUPP;
224502e84e2SFelix Fietkau 
2258bd8dcc5SFelix Fietkau 	if (dsa_port >= 0) {
2268bd8dcc5SFelix Fietkau 		mtk_foe_entry_set_dsa(eth, foe, dsa_port);
2278bd8dcc5SFelix Fietkau 		queue = 3 + dsa_port;
2288bd8dcc5SFelix Fietkau 	} else {
2298bd8dcc5SFelix Fietkau 		queue = pse_port - 1;
2308bd8dcc5SFelix Fietkau 	}
2318bd8dcc5SFelix Fietkau 	mtk_foe_entry_set_queue(eth, foe, queue);
2328bd8dcc5SFelix Fietkau 
233a333215eSFelix Fietkau out:
23403a3180eSLorenzo Bianconi 	mtk_foe_entry_set_pse_port(eth, foe, pse_port);
235502e84e2SFelix Fietkau 
236502e84e2SFelix Fietkau 	return 0;
237502e84e2SFelix Fietkau }
238502e84e2SFelix Fietkau 
239502e84e2SFelix Fietkau static int
mtk_flow_offload_replace(struct mtk_eth * eth,struct flow_cls_offload * f,int ppe_index)24005f3ab77SFelix Fietkau mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
24105f3ab77SFelix Fietkau 			 int ppe_index)
242502e84e2SFelix Fietkau {
243502e84e2SFelix Fietkau 	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
244502e84e2SFelix Fietkau 	struct flow_action_entry *act;
245502e84e2SFelix Fietkau 	struct mtk_flow_data data = {};
246502e84e2SFelix Fietkau 	struct mtk_foe_entry foe;
247502e84e2SFelix Fietkau 	struct net_device *odev = NULL;
248502e84e2SFelix Fietkau 	struct mtk_flow_entry *entry;
249502e84e2SFelix Fietkau 	int offload_type = 0;
250a333215eSFelix Fietkau 	int wed_index = -1;
251502e84e2SFelix Fietkau 	u16 addr_type = 0;
252502e84e2SFelix Fietkau 	u8 l4proto = 0;
253502e84e2SFelix Fietkau 	int err = 0;
254502e84e2SFelix Fietkau 	int i;
255502e84e2SFelix Fietkau 
256e68daf61SFelix Fietkau 	if (rhashtable_lookup(&eth->flow_table, &f->cookie, mtk_flow_ht_params))
257e68daf61SFelix Fietkau 		return -EEXIST;
258e68daf61SFelix Fietkau 
259502e84e2SFelix Fietkau 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) {
260502e84e2SFelix Fietkau 		struct flow_match_meta match;
261502e84e2SFelix Fietkau 
262502e84e2SFelix Fietkau 		flow_rule_match_meta(rule, &match);
263502e84e2SFelix Fietkau 	} else {
264502e84e2SFelix Fietkau 		return -EOPNOTSUPP;
265502e84e2SFelix Fietkau 	}
266502e84e2SFelix Fietkau 
267502e84e2SFelix Fietkau 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
268502e84e2SFelix Fietkau 		struct flow_match_control match;
269502e84e2SFelix Fietkau 
270502e84e2SFelix Fietkau 		flow_rule_match_control(rule, &match);
271502e84e2SFelix Fietkau 		addr_type = match.key->addr_type;
272502e84e2SFelix Fietkau 	} else {
273502e84e2SFelix Fietkau 		return -EOPNOTSUPP;
274502e84e2SFelix Fietkau 	}
275502e84e2SFelix Fietkau 
276502e84e2SFelix Fietkau 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
277502e84e2SFelix Fietkau 		struct flow_match_basic match;
278502e84e2SFelix Fietkau 
279502e84e2SFelix Fietkau 		flow_rule_match_basic(rule, &match);
280502e84e2SFelix Fietkau 		l4proto = match.key->ip_proto;
281502e84e2SFelix Fietkau 	} else {
282502e84e2SFelix Fietkau 		return -EOPNOTSUPP;
283502e84e2SFelix Fietkau 	}
284502e84e2SFelix Fietkau 
28533fc42deSFelix Fietkau 	switch (addr_type) {
28633fc42deSFelix Fietkau 	case 0:
28733fc42deSFelix Fietkau 		offload_type = MTK_PPE_PKT_TYPE_BRIDGE;
28833fc42deSFelix Fietkau 		if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
28933fc42deSFelix Fietkau 			struct flow_match_eth_addrs match;
29033fc42deSFelix Fietkau 
29133fc42deSFelix Fietkau 			flow_rule_match_eth_addrs(rule, &match);
29233fc42deSFelix Fietkau 			memcpy(data.eth.h_dest, match.key->dst, ETH_ALEN);
29333fc42deSFelix Fietkau 			memcpy(data.eth.h_source, match.key->src, ETH_ALEN);
29433fc42deSFelix Fietkau 		} else {
29533fc42deSFelix Fietkau 			return -EOPNOTSUPP;
29633fc42deSFelix Fietkau 		}
29733fc42deSFelix Fietkau 
29833fc42deSFelix Fietkau 		if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
29933fc42deSFelix Fietkau 			struct flow_match_vlan match;
30033fc42deSFelix Fietkau 
30133fc42deSFelix Fietkau 			flow_rule_match_vlan(rule, &match);
30233fc42deSFelix Fietkau 
30333fc42deSFelix Fietkau 			if (match.key->vlan_tpid != cpu_to_be16(ETH_P_8021Q))
30433fc42deSFelix Fietkau 				return -EOPNOTSUPP;
30533fc42deSFelix Fietkau 
30633fc42deSFelix Fietkau 			data.vlan_in = match.key->vlan_id;
30733fc42deSFelix Fietkau 		}
30833fc42deSFelix Fietkau 		break;
30933fc42deSFelix Fietkau 	case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
31033fc42deSFelix Fietkau 		offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
31133fc42deSFelix Fietkau 		break;
31233fc42deSFelix Fietkau 	case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
31333fc42deSFelix Fietkau 		offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
31433fc42deSFelix Fietkau 		break;
31533fc42deSFelix Fietkau 	default:
31633fc42deSFelix Fietkau 		return -EOPNOTSUPP;
31733fc42deSFelix Fietkau 	}
31833fc42deSFelix Fietkau 
319502e84e2SFelix Fietkau 	flow_action_for_each(i, act, &rule->action) {
320502e84e2SFelix Fietkau 		switch (act->id) {
321502e84e2SFelix Fietkau 		case FLOW_ACTION_MANGLE:
32233fc42deSFelix Fietkau 			if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
32333fc42deSFelix Fietkau 				return -EOPNOTSUPP;
324502e84e2SFelix Fietkau 			if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
325502e84e2SFelix Fietkau 				mtk_flow_offload_mangle_eth(act, &data.eth);
326502e84e2SFelix Fietkau 			break;
327502e84e2SFelix Fietkau 		case FLOW_ACTION_REDIRECT:
328502e84e2SFelix Fietkau 			odev = act->dev;
329502e84e2SFelix Fietkau 			break;
330502e84e2SFelix Fietkau 		case FLOW_ACTION_CSUM:
331502e84e2SFelix Fietkau 			break;
332502e84e2SFelix Fietkau 		case FLOW_ACTION_VLAN_PUSH:
333502e84e2SFelix Fietkau 			if (data.vlan.num == 1 ||
334502e84e2SFelix Fietkau 			    act->vlan.proto != htons(ETH_P_8021Q))
335502e84e2SFelix Fietkau 				return -EOPNOTSUPP;
336502e84e2SFelix Fietkau 
337502e84e2SFelix Fietkau 			data.vlan.id = act->vlan.vid;
338502e84e2SFelix Fietkau 			data.vlan.proto = act->vlan.proto;
339502e84e2SFelix Fietkau 			data.vlan.num++;
340502e84e2SFelix Fietkau 			break;
341f5c2cb58SPablo Neira Ayuso 		case FLOW_ACTION_VLAN_POP:
342f5c2cb58SPablo Neira Ayuso 			break;
343502e84e2SFelix Fietkau 		case FLOW_ACTION_PPPOE_PUSH:
344502e84e2SFelix Fietkau 			if (data.pppoe.num == 1)
345502e84e2SFelix Fietkau 				return -EOPNOTSUPP;
346502e84e2SFelix Fietkau 
347502e84e2SFelix Fietkau 			data.pppoe.sid = act->pppoe.sid;
348502e84e2SFelix Fietkau 			data.pppoe.num++;
349502e84e2SFelix Fietkau 			break;
350502e84e2SFelix Fietkau 		default:
351502e84e2SFelix Fietkau 			return -EOPNOTSUPP;
352502e84e2SFelix Fietkau 		}
353502e84e2SFelix Fietkau 	}
354502e84e2SFelix Fietkau 
355502e84e2SFelix Fietkau 	if (!is_valid_ether_addr(data.eth.h_source) ||
356502e84e2SFelix Fietkau 	    !is_valid_ether_addr(data.eth.h_dest))
357502e84e2SFelix Fietkau 		return -EINVAL;
358502e84e2SFelix Fietkau 
35903a3180eSLorenzo Bianconi 	err = mtk_foe_entry_prepare(eth, &foe, offload_type, l4proto, 0,
36003a3180eSLorenzo Bianconi 				    data.eth.h_source, data.eth.h_dest);
361502e84e2SFelix Fietkau 	if (err)
362502e84e2SFelix Fietkau 		return err;
363502e84e2SFelix Fietkau 
364502e84e2SFelix Fietkau 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
365502e84e2SFelix Fietkau 		struct flow_match_ports ports;
366502e84e2SFelix Fietkau 
36733fc42deSFelix Fietkau 		if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
36833fc42deSFelix Fietkau 			return -EOPNOTSUPP;
36933fc42deSFelix Fietkau 
370502e84e2SFelix Fietkau 		flow_rule_match_ports(rule, &ports);
371502e84e2SFelix Fietkau 		data.src_port = ports.key->src;
372502e84e2SFelix Fietkau 		data.dst_port = ports.key->dst;
37333fc42deSFelix Fietkau 	} else if (offload_type != MTK_PPE_PKT_TYPE_BRIDGE) {
374502e84e2SFelix Fietkau 		return -EOPNOTSUPP;
375502e84e2SFelix Fietkau 	}
376502e84e2SFelix Fietkau 
377502e84e2SFelix Fietkau 	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
378502e84e2SFelix Fietkau 		struct flow_match_ipv4_addrs addrs;
379502e84e2SFelix Fietkau 
380502e84e2SFelix Fietkau 		flow_rule_match_ipv4_addrs(rule, &addrs);
381502e84e2SFelix Fietkau 
382502e84e2SFelix Fietkau 		data.v4.src_addr = addrs.key->src;
383502e84e2SFelix Fietkau 		data.v4.dst_addr = addrs.key->dst;
384502e84e2SFelix Fietkau 
38503a3180eSLorenzo Bianconi 		mtk_flow_set_ipv4_addr(eth, &foe, &data, false);
386502e84e2SFelix Fietkau 	}
387502e84e2SFelix Fietkau 
388817b2fdfSDavid Bentham 	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
389817b2fdfSDavid Bentham 		struct flow_match_ipv6_addrs addrs;
390817b2fdfSDavid Bentham 
391817b2fdfSDavid Bentham 		flow_rule_match_ipv6_addrs(rule, &addrs);
392817b2fdfSDavid Bentham 
393817b2fdfSDavid Bentham 		data.v6.src_addr = addrs.key->src;
394817b2fdfSDavid Bentham 		data.v6.dst_addr = addrs.key->dst;
395817b2fdfSDavid Bentham 
39603a3180eSLorenzo Bianconi 		mtk_flow_set_ipv6_addr(eth, &foe, &data);
397817b2fdfSDavid Bentham 	}
398817b2fdfSDavid Bentham 
399502e84e2SFelix Fietkau 	flow_action_for_each(i, act, &rule->action) {
400502e84e2SFelix Fietkau 		if (act->id != FLOW_ACTION_MANGLE)
401502e84e2SFelix Fietkau 			continue;
402502e84e2SFelix Fietkau 
40333fc42deSFelix Fietkau 		if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
40433fc42deSFelix Fietkau 			return -EOPNOTSUPP;
40533fc42deSFelix Fietkau 
406502e84e2SFelix Fietkau 		switch (act->mangle.htype) {
407502e84e2SFelix Fietkau 		case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
408502e84e2SFelix Fietkau 		case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
409502e84e2SFelix Fietkau 			err = mtk_flow_mangle_ports(act, &data);
410502e84e2SFelix Fietkau 			break;
411502e84e2SFelix Fietkau 		case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
412502e84e2SFelix Fietkau 			err = mtk_flow_mangle_ipv4(act, &data);
413502e84e2SFelix Fietkau 			break;
414502e84e2SFelix Fietkau 		case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
415502e84e2SFelix Fietkau 			/* handled earlier */
416502e84e2SFelix Fietkau 			break;
417502e84e2SFelix Fietkau 		default:
418502e84e2SFelix Fietkau 			return -EOPNOTSUPP;
419502e84e2SFelix Fietkau 		}
420502e84e2SFelix Fietkau 
421502e84e2SFelix Fietkau 		if (err)
422502e84e2SFelix Fietkau 			return err;
423502e84e2SFelix Fietkau 	}
424502e84e2SFelix Fietkau 
425502e84e2SFelix Fietkau 	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
42603a3180eSLorenzo Bianconi 		err = mtk_flow_set_ipv4_addr(eth, &foe, &data, true);
427502e84e2SFelix Fietkau 		if (err)
428502e84e2SFelix Fietkau 			return err;
429502e84e2SFelix Fietkau 	}
430502e84e2SFelix Fietkau 
43133fc42deSFelix Fietkau 	if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
43233fc42deSFelix Fietkau 		foe.bridge.vlan = data.vlan_in;
43333fc42deSFelix Fietkau 
434502e84e2SFelix Fietkau 	if (data.vlan.num == 1) {
435502e84e2SFelix Fietkau 		if (data.vlan.proto != htons(ETH_P_8021Q))
436502e84e2SFelix Fietkau 			return -EOPNOTSUPP;
437502e84e2SFelix Fietkau 
43803a3180eSLorenzo Bianconi 		mtk_foe_entry_set_vlan(eth, &foe, data.vlan.id);
439502e84e2SFelix Fietkau 	}
440502e84e2SFelix Fietkau 	if (data.pppoe.num == 1)
44103a3180eSLorenzo Bianconi 		mtk_foe_entry_set_pppoe(eth, &foe, data.pppoe.sid);
442502e84e2SFelix Fietkau 
443a333215eSFelix Fietkau 	err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest,
444a333215eSFelix Fietkau 					 &wed_index);
445502e84e2SFelix Fietkau 	if (err)
446502e84e2SFelix Fietkau 		return err;
447502e84e2SFelix Fietkau 
448a333215eSFelix Fietkau 	if (wed_index >= 0 && (err = mtk_wed_flow_add(wed_index)) < 0)
449a333215eSFelix Fietkau 		return err;
450a333215eSFelix Fietkau 
451502e84e2SFelix Fietkau 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
452502e84e2SFelix Fietkau 	if (!entry)
453502e84e2SFelix Fietkau 		return -ENOMEM;
454502e84e2SFelix Fietkau 
455502e84e2SFelix Fietkau 	entry->cookie = f->cookie;
456c4f033d9SFelix Fietkau 	memcpy(&entry->data, &foe, sizeof(entry->data));
457a333215eSFelix Fietkau 	entry->wed_index = wed_index;
45805f3ab77SFelix Fietkau 	entry->ppe_index = ppe_index;
459c4f033d9SFelix Fietkau 
4604ff1a3fcSLorenzo Bianconi 	err = mtk_foe_entry_commit(eth->ppe[entry->ppe_index], entry);
4610097e86cSDan Carpenter 	if (err < 0)
462c4f033d9SFelix Fietkau 		goto free;
463c4f033d9SFelix Fietkau 
464502e84e2SFelix Fietkau 	err = rhashtable_insert_fast(&eth->flow_table, &entry->node,
465502e84e2SFelix Fietkau 				     mtk_flow_ht_params);
466502e84e2SFelix Fietkau 	if (err < 0)
467c4f033d9SFelix Fietkau 		goto clear;
468502e84e2SFelix Fietkau 
469502e84e2SFelix Fietkau 	return 0;
470c4f033d9SFelix Fietkau 
471c4f033d9SFelix Fietkau clear:
4724ff1a3fcSLorenzo Bianconi 	mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry);
473502e84e2SFelix Fietkau free:
474502e84e2SFelix Fietkau 	kfree(entry);
475a333215eSFelix Fietkau 	if (wed_index >= 0)
476a333215eSFelix Fietkau 	    mtk_wed_flow_remove(wed_index);
477502e84e2SFelix Fietkau 	return err;
478502e84e2SFelix Fietkau }
479502e84e2SFelix Fietkau 
480502e84e2SFelix Fietkau static int
mtk_flow_offload_destroy(struct mtk_eth * eth,struct flow_cls_offload * f)481502e84e2SFelix Fietkau mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f)
482502e84e2SFelix Fietkau {
483502e84e2SFelix Fietkau 	struct mtk_flow_entry *entry;
484502e84e2SFelix Fietkau 
485502e84e2SFelix Fietkau 	entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
486502e84e2SFelix Fietkau 				  mtk_flow_ht_params);
487502e84e2SFelix Fietkau 	if (!entry)
488502e84e2SFelix Fietkau 		return -ENOENT;
489502e84e2SFelix Fietkau 
4904ff1a3fcSLorenzo Bianconi 	mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry);
491502e84e2SFelix Fietkau 	rhashtable_remove_fast(&eth->flow_table, &entry->node,
492502e84e2SFelix Fietkau 			       mtk_flow_ht_params);
493a333215eSFelix Fietkau 	if (entry->wed_index >= 0)
494a333215eSFelix Fietkau 		mtk_wed_flow_remove(entry->wed_index);
495502e84e2SFelix Fietkau 	kfree(entry);
496502e84e2SFelix Fietkau 
497502e84e2SFelix Fietkau 	return 0;
498502e84e2SFelix Fietkau }
499502e84e2SFelix Fietkau 
500502e84e2SFelix Fietkau static int
mtk_flow_offload_stats(struct mtk_eth * eth,struct flow_cls_offload * f)501502e84e2SFelix Fietkau mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
502502e84e2SFelix Fietkau {
503502e84e2SFelix Fietkau 	struct mtk_flow_entry *entry;
5043fbe4d8cSDaniel Golle 	struct mtk_foe_accounting diff;
505502e84e2SFelix Fietkau 	u32 idle;
506502e84e2SFelix Fietkau 
507502e84e2SFelix Fietkau 	entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
508502e84e2SFelix Fietkau 				  mtk_flow_ht_params);
509502e84e2SFelix Fietkau 	if (!entry)
510502e84e2SFelix Fietkau 		return -ENOENT;
511502e84e2SFelix Fietkau 
5124ff1a3fcSLorenzo Bianconi 	idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
513502e84e2SFelix Fietkau 	f->stats.lastused = jiffies - idle * HZ;
514502e84e2SFelix Fietkau 
5153fbe4d8cSDaniel Golle 	if (entry->hash != 0xFFFF &&
5163fbe4d8cSDaniel Golle 	    mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash,
5173fbe4d8cSDaniel Golle 				  &diff)) {
5183fbe4d8cSDaniel Golle 		f->stats.pkts += diff.packets;
5193fbe4d8cSDaniel Golle 		f->stats.bytes += diff.bytes;
5203fbe4d8cSDaniel Golle 	}
5213fbe4d8cSDaniel Golle 
522502e84e2SFelix Fietkau 	return 0;
523502e84e2SFelix Fietkau }
524502e84e2SFelix Fietkau 
525014d0298SPablo Neira Ayuso static DEFINE_MUTEX(mtk_flow_offload_mutex);
526014d0298SPablo Neira Ayuso 
mtk_flow_offload_cmd(struct mtk_eth * eth,struct flow_cls_offload * cls,int ppe_index)52705f3ab77SFelix Fietkau int mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls,
52805f3ab77SFelix Fietkau 			 int ppe_index)
529502e84e2SFelix Fietkau {
530014d0298SPablo Neira Ayuso 	int err;
531502e84e2SFelix Fietkau 
532014d0298SPablo Neira Ayuso 	mutex_lock(&mtk_flow_offload_mutex);
533502e84e2SFelix Fietkau 	switch (cls->command) {
534502e84e2SFelix Fietkau 	case FLOW_CLS_REPLACE:
53505f3ab77SFelix Fietkau 		err = mtk_flow_offload_replace(eth, cls, ppe_index);
536014d0298SPablo Neira Ayuso 		break;
537502e84e2SFelix Fietkau 	case FLOW_CLS_DESTROY:
538014d0298SPablo Neira Ayuso 		err = mtk_flow_offload_destroy(eth, cls);
539014d0298SPablo Neira Ayuso 		break;
540502e84e2SFelix Fietkau 	case FLOW_CLS_STATS:
541014d0298SPablo Neira Ayuso 		err = mtk_flow_offload_stats(eth, cls);
542014d0298SPablo Neira Ayuso 		break;
543502e84e2SFelix Fietkau 	default:
544014d0298SPablo Neira Ayuso 		err = -EOPNOTSUPP;
545014d0298SPablo Neira Ayuso 		break;
546502e84e2SFelix Fietkau 	}
547014d0298SPablo Neira Ayuso 	mutex_unlock(&mtk_flow_offload_mutex);
548502e84e2SFelix Fietkau 
549014d0298SPablo Neira Ayuso 	return err;
550502e84e2SFelix Fietkau }
551502e84e2SFelix Fietkau 
552502e84e2SFelix Fietkau static int
mtk_eth_setup_tc_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv)55305f3ab77SFelix Fietkau mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
55405f3ab77SFelix Fietkau {
55505f3ab77SFelix Fietkau 	struct flow_cls_offload *cls = type_data;
55605f3ab77SFelix Fietkau 	struct net_device *dev = cb_priv;
55705f3ab77SFelix Fietkau 	struct mtk_mac *mac;
55805f3ab77SFelix Fietkau 	struct mtk_eth *eth;
55905f3ab77SFelix Fietkau 
56005f3ab77SFelix Fietkau 	mac = netdev_priv(dev);
56105f3ab77SFelix Fietkau 	eth = mac->hw;
56205f3ab77SFelix Fietkau 
56305f3ab77SFelix Fietkau 	if (!tc_can_offload(dev))
56405f3ab77SFelix Fietkau 		return -EOPNOTSUPP;
56505f3ab77SFelix Fietkau 
56605f3ab77SFelix Fietkau 	if (type != TC_SETUP_CLSFLOWER)
56705f3ab77SFelix Fietkau 		return -EOPNOTSUPP;
56805f3ab77SFelix Fietkau 
56905f3ab77SFelix Fietkau 	return mtk_flow_offload_cmd(eth, cls, 0);
57005f3ab77SFelix Fietkau }
57105f3ab77SFelix Fietkau 
57205f3ab77SFelix Fietkau static int
mtk_eth_setup_tc_block(struct net_device * dev,struct flow_block_offload * f)573502e84e2SFelix Fietkau mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
574502e84e2SFelix Fietkau {
575502e84e2SFelix Fietkau 	struct mtk_mac *mac = netdev_priv(dev);
576502e84e2SFelix Fietkau 	struct mtk_eth *eth = mac->hw;
577502e84e2SFelix Fietkau 	static LIST_HEAD(block_cb_list);
578502e84e2SFelix Fietkau 	struct flow_block_cb *block_cb;
579502e84e2SFelix Fietkau 	flow_setup_cb_t *cb;
580502e84e2SFelix Fietkau 
5814ff1a3fcSLorenzo Bianconi 	if (!eth->soc->offload_version)
582502e84e2SFelix Fietkau 		return -EOPNOTSUPP;
583502e84e2SFelix Fietkau 
584502e84e2SFelix Fietkau 	if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
585502e84e2SFelix Fietkau 		return -EOPNOTSUPP;
586502e84e2SFelix Fietkau 
587502e84e2SFelix Fietkau 	cb = mtk_eth_setup_tc_block_cb;
588502e84e2SFelix Fietkau 	f->driver_block_list = &block_cb_list;
589502e84e2SFelix Fietkau 
590502e84e2SFelix Fietkau 	switch (f->command) {
591502e84e2SFelix Fietkau 	case FLOW_BLOCK_BIND:
592502e84e2SFelix Fietkau 		block_cb = flow_block_cb_lookup(f->block, cb, dev);
593502e84e2SFelix Fietkau 		if (block_cb) {
594502e84e2SFelix Fietkau 			flow_block_cb_incref(block_cb);
595502e84e2SFelix Fietkau 			return 0;
596502e84e2SFelix Fietkau 		}
597502e84e2SFelix Fietkau 		block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
598502e84e2SFelix Fietkau 		if (IS_ERR(block_cb))
599502e84e2SFelix Fietkau 			return PTR_ERR(block_cb);
600502e84e2SFelix Fietkau 
6018c1cb87cSFelix Fietkau 		flow_block_cb_incref(block_cb);
602502e84e2SFelix Fietkau 		flow_block_cb_add(block_cb, f);
603502e84e2SFelix Fietkau 		list_add_tail(&block_cb->driver_list, &block_cb_list);
604502e84e2SFelix Fietkau 		return 0;
605502e84e2SFelix Fietkau 	case FLOW_BLOCK_UNBIND:
606502e84e2SFelix Fietkau 		block_cb = flow_block_cb_lookup(f->block, cb, dev);
607502e84e2SFelix Fietkau 		if (!block_cb)
608502e84e2SFelix Fietkau 			return -ENOENT;
609502e84e2SFelix Fietkau 
6108c1cb87cSFelix Fietkau 		if (!flow_block_cb_decref(block_cb)) {
611502e84e2SFelix Fietkau 			flow_block_cb_remove(block_cb, f);
612502e84e2SFelix Fietkau 			list_del(&block_cb->driver_list);
613502e84e2SFelix Fietkau 		}
614502e84e2SFelix Fietkau 		return 0;
615502e84e2SFelix Fietkau 	default:
616502e84e2SFelix Fietkau 		return -EOPNOTSUPP;
617502e84e2SFelix Fietkau 	}
618502e84e2SFelix Fietkau }
619502e84e2SFelix Fietkau 
mtk_eth_setup_tc(struct net_device * dev,enum tc_setup_type type,void * type_data)620502e84e2SFelix Fietkau int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
621502e84e2SFelix Fietkau 		     void *type_data)
622502e84e2SFelix Fietkau {
623bb14c191SFelix Fietkau 	switch (type) {
624bb14c191SFelix Fietkau 	case TC_SETUP_BLOCK:
625bb14c191SFelix Fietkau 	case TC_SETUP_FT:
626502e84e2SFelix Fietkau 		return mtk_eth_setup_tc_block(dev, type_data);
627bb14c191SFelix Fietkau 	default:
628502e84e2SFelix Fietkau 		return -EOPNOTSUPP;
629502e84e2SFelix Fietkau 	}
630bb14c191SFelix Fietkau }
631502e84e2SFelix Fietkau 
mtk_eth_offload_init(struct mtk_eth * eth)632502e84e2SFelix Fietkau int mtk_eth_offload_init(struct mtk_eth *eth)
633502e84e2SFelix Fietkau {
634502e84e2SFelix Fietkau 	return rhashtable_init(&eth->flow_table, &mtk_flow_ht_params);
635502e84e2SFelix Fietkau }
636