1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip VCAP API
3  *
4  * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
5  */
6 
7 #include <net/tcp.h>
8 
9 #include "sparx5_tc.h"
10 #include "vcap_api.h"
11 #include "vcap_api_client.h"
12 #include "sparx5_main.h"
13 #include "sparx5_vcap_impl.h"
14 
15 struct sparx5_tc_flower_parse_usage {
16 	struct flow_cls_offload *fco;
17 	struct flow_rule *frule;
18 	struct vcap_rule *vrule;
19 	unsigned int used_keys;
20 };
21 
22 static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st)
23 {
24 	enum vcap_key_field smac_key = VCAP_KF_L2_SMAC;
25 	enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC;
26 	struct flow_match_eth_addrs match;
27 	struct vcap_u48_key smac, dmac;
28 	int err = 0;
29 
30 	flow_rule_match_eth_addrs(st->frule, &match);
31 
32 	if (!is_zero_ether_addr(match.mask->src)) {
33 		vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN);
34 		vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN);
35 		err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac);
36 		if (err)
37 			goto out;
38 	}
39 
40 	if (!is_zero_ether_addr(match.mask->dst)) {
41 		vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN);
42 		vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN);
43 		err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac);
44 		if (err)
45 			goto out;
46 	}
47 
48 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
49 
50 	return err;
51 
52 out:
53 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error");
54 	return err;
55 }
56 
57 static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = {
58 	/* More dissector handlers will be added here later */
59 	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage,
60 };
61 
62 static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
63 				    struct vcap_admin *admin,
64 				    struct vcap_rule *vrule)
65 {
66 	struct sparx5_tc_flower_parse_usage state = {
67 		.fco = fco,
68 		.vrule = vrule,
69 	};
70 	int idx, err = 0;
71 
72 	state.frule = flow_cls_offload_flow_rule(fco);
73 	for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {
74 		if (!flow_rule_match_key(state.frule, idx))
75 			continue;
76 		if (!sparx5_tc_flower_usage_handlers[idx])
77 			continue;
78 		err = sparx5_tc_flower_usage_handlers[idx](&state);
79 		if (err)
80 			return err;
81 	}
82 	return err;
83 }
84 
85 static int sparx5_tc_flower_replace(struct net_device *ndev,
86 				    struct flow_cls_offload *fco,
87 				    struct vcap_admin *admin)
88 {
89 	struct sparx5_port *port = netdev_priv(ndev);
90 	struct flow_action_entry *act;
91 	struct vcap_control *vctrl;
92 	struct flow_rule *frule;
93 	struct vcap_rule *vrule;
94 	int err, idx;
95 
96 	frule = flow_cls_offload_flow_rule(fco);
97 	if (!flow_action_has_entries(&frule->action)) {
98 		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
99 		return -EINVAL;
100 	}
101 
102 	if (!flow_action_basic_hw_stats_check(&frule->action, fco->common.extack))
103 		return -EOPNOTSUPP;
104 
105 	vctrl = port->sparx5->vcap_ctrl;
106 	vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC,
107 				fco->common.prio, 0);
108 	if (IS_ERR(vrule))
109 		return PTR_ERR(vrule);
110 
111 	vrule->cookie = fco->cookie;
112 	sparx5_tc_use_dissectors(fco, admin, vrule);
113 	flow_action_for_each(idx, act, &frule->action) {
114 		switch (act->id) {
115 		case FLOW_ACTION_TRAP:
116 			err = vcap_rule_add_action_bit(vrule,
117 						       VCAP_AF_CPU_COPY_ENA,
118 						       VCAP_BIT_1);
119 			if (err)
120 				goto out;
121 			err = vcap_rule_add_action_u32(vrule,
122 						       VCAP_AF_CPU_QUEUE_NUM, 0);
123 			if (err)
124 				goto out;
125 			err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
126 						       SPX5_PMM_REPLACE_ALL);
127 			if (err)
128 				goto out;
129 			/* For now the actionset is hardcoded */
130 			err = vcap_set_rule_set_actionset(vrule,
131 							  VCAP_AFS_BASE_TYPE);
132 			if (err)
133 				goto out;
134 			break;
135 		case FLOW_ACTION_ACCEPT:
136 			/* For now the actionset is hardcoded */
137 			err = vcap_set_rule_set_actionset(vrule,
138 							  VCAP_AFS_BASE_TYPE);
139 			if (err)
140 				goto out;
141 			break;
142 		default:
143 			NL_SET_ERR_MSG_MOD(fco->common.extack,
144 					   "Unsupported TC action");
145 			err = -EOPNOTSUPP;
146 			goto out;
147 		}
148 	}
149 	/* For now the keyset is hardcoded */
150 	err = vcap_set_rule_set_keyset(vrule, VCAP_KFS_MAC_ETYPE);
151 	if (err) {
152 		NL_SET_ERR_MSG_MOD(fco->common.extack,
153 				   "No matching port keyset for filter protocol and keys");
154 		goto out;
155 	}
156 	err = vcap_val_rule(vrule, ETH_P_ALL);
157 	if (err) {
158 		vcap_set_tc_exterr(fco, vrule);
159 		goto out;
160 	}
161 	err = vcap_add_rule(vrule);
162 	if (err)
163 		NL_SET_ERR_MSG_MOD(fco->common.extack,
164 				   "Could not add the filter");
165 out:
166 	vcap_free_rule(vrule);
167 	return err;
168 }
169 
170 static int sparx5_tc_flower_destroy(struct net_device *ndev,
171 				    struct flow_cls_offload *fco,
172 				    struct vcap_admin *admin)
173 {
174 	struct sparx5_port *port = netdev_priv(ndev);
175 	struct vcap_control *vctrl;
176 	int err = -ENOENT, rule_id;
177 
178 	vctrl = port->sparx5->vcap_ctrl;
179 	while (true) {
180 		rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie);
181 		if (rule_id <= 0)
182 			break;
183 		err = vcap_del_rule(vctrl, ndev, rule_id);
184 		if (err) {
185 			pr_err("%s:%d: could not delete rule %d\n",
186 			       __func__, __LINE__, rule_id);
187 			break;
188 		}
189 	}
190 	return err;
191 }
192 
193 int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
194 		     bool ingress)
195 {
196 	struct sparx5_port *port = netdev_priv(ndev);
197 	struct vcap_control *vctrl;
198 	struct vcap_admin *admin;
199 	int err = -EINVAL;
200 
201 	/* Get vcap instance from the chain id */
202 	vctrl = port->sparx5->vcap_ctrl;
203 	admin = vcap_find_admin(vctrl, fco->common.chain_index);
204 	if (!admin) {
205 		NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain");
206 		return err;
207 	}
208 
209 	switch (fco->command) {
210 	case FLOW_CLS_REPLACE:
211 		return sparx5_tc_flower_replace(ndev, fco, admin);
212 	case FLOW_CLS_DESTROY:
213 		return sparx5_tc_flower_destroy(ndev, fco, admin);
214 	default:
215 		return -EOPNOTSUPP;
216 	}
217 }
218