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(ð->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(ð->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(ð->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(ð->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(ð->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(ð->flow_table, &mtk_flow_ht_params);
635502e84e2SFelix Fietkau }
636