xref: /linux/drivers/net/dsa/microchip/ksz9477_tc_flower.c (revision 6e7fd890f1d6ac83805409e9c346240de2705584)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
3 
4 #include "ksz9477.h"
5 #include "ksz9477_reg.h"
6 #include "ksz_common.h"
7 
8 #define ETHER_TYPE_FULL_MASK		cpu_to_be16(~0)
9 #define KSZ9477_MAX_TC			7
10 
11 /**
12  * ksz9477_flower_parse_key_l2 - Parse Layer 2 key from flow rule and configure
13  *                               ACL entries accordingly.
14  * @dev: Pointer to the ksz_device.
15  * @port: Port number.
16  * @extack: Pointer to the netlink_ext_ack.
17  * @rule: Pointer to the flow_rule.
18  * @cookie: The cookie to associate with the entry.
19  * @prio: The priority of the entry.
20  *
21  * This function parses the Layer 2 key from the flow rule and configures
22  * the corresponding ACL entries. It checks for unsupported offloads and
23  * available entries before proceeding with the configuration.
24  *
25  * Returns: 0 on success or a negative error code on failure.
26  */
27 static int ksz9477_flower_parse_key_l2(struct ksz_device *dev, int port,
28 				       struct netlink_ext_ack *extack,
29 				       struct flow_rule *rule,
30 				       unsigned long cookie, u32 prio)
31 {
32 	struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
33 	struct flow_match_eth_addrs ematch;
34 	struct ksz9477_acl_entries *acles;
35 	int required_entries;
36 	u8 *src_mac = NULL;
37 	u8 *dst_mac = NULL;
38 	u16 ethtype = 0;
39 
40 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
41 		struct flow_match_basic match;
42 
43 		flow_rule_match_basic(rule, &match);
44 
45 		if (match.key->n_proto) {
46 			if (match.mask->n_proto != ETHER_TYPE_FULL_MASK) {
47 				NL_SET_ERR_MSG_MOD(extack,
48 						   "ethernet type mask must be a full mask");
49 				return -EINVAL;
50 			}
51 
52 			ethtype = be16_to_cpu(match.key->n_proto);
53 		}
54 	}
55 
56 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
57 		flow_rule_match_eth_addrs(rule, &ematch);
58 
59 		if (!is_zero_ether_addr(ematch.key->src)) {
60 			if (!is_broadcast_ether_addr(ematch.mask->src))
61 				goto not_full_mask_err;
62 
63 			src_mac = ematch.key->src;
64 		}
65 
66 		if (!is_zero_ether_addr(ematch.key->dst)) {
67 			if (!is_broadcast_ether_addr(ematch.mask->dst))
68 				goto not_full_mask_err;
69 
70 			dst_mac = ematch.key->dst;
71 		}
72 	}
73 
74 	acles = &acl->acles;
75 	/* ACL supports only one MAC per entry */
76 	required_entries = src_mac && dst_mac ? 2 : 1;
77 
78 	/* Check if there are enough available entries */
79 	if (acles->entries_count + required_entries > KSZ9477_ACL_MAX_ENTRIES) {
80 		NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached");
81 		return -EOPNOTSUPP;
82 	}
83 
84 	ksz9477_acl_match_process_l2(dev, port, ethtype, src_mac, dst_mac,
85 				     cookie, prio);
86 
87 	return 0;
88 
89 not_full_mask_err:
90 	NL_SET_ERR_MSG_MOD(extack, "MAC address mask must be a full mask");
91 	return -EOPNOTSUPP;
92 }
93 
94 /**
95  * ksz9477_flower_parse_key - Parse flow rule keys for a specified port on a
96  *			      ksz_device.
97  * @dev: The ksz_device instance.
98  * @port: The port number to parse the flow rule keys for.
99  * @extack: The netlink extended ACK for reporting errors.
100  * @rule: The flow_rule to parse.
101  * @cookie: The cookie to associate with the entry.
102  * @prio: The priority of the entry.
103  *
104  * This function checks if the used keys in the flow rule are supported by
105  * the device and parses the L2 keys if they match. If unsupported keys are
106  * used, an error message is set in the extended ACK.
107  *
108  * Returns: 0 on success or a negative error code on failure.
109  */
110 static int ksz9477_flower_parse_key(struct ksz_device *dev, int port,
111 				    struct netlink_ext_ack *extack,
112 				    struct flow_rule *rule,
113 				    unsigned long cookie, u32 prio)
114 {
115 	struct flow_dissector *dissector = rule->match.dissector;
116 	int ret;
117 
118 	if (dissector->used_keys &
119 	    ~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
120 	      BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
121 	      BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL))) {
122 		NL_SET_ERR_MSG_MOD(extack,
123 				   "Unsupported keys used");
124 		return -EOPNOTSUPP;
125 	}
126 
127 	if (flow_rule_match_has_control_flags(rule, extack))
128 		return -EOPNOTSUPP;
129 
130 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC) ||
131 	    flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
132 		ret = ksz9477_flower_parse_key_l2(dev, port, extack, rule,
133 						  cookie, prio);
134 		if (ret)
135 			return ret;
136 	}
137 
138 	return 0;
139 }
140 
141 /**
142  * ksz9477_flower_parse_action - Parse flow rule actions for a specified port
143  *				 on a ksz_device.
144  * @dev: The ksz_device instance.
145  * @port: The port number to parse the flow rule actions for.
146  * @extack: The netlink extended ACK for reporting errors.
147  * @cls: The flow_cls_offload instance containing the flow rule.
148  * @entry_idx: The index of the ACL entry to store the action.
149  *
150  * This function checks if the actions in the flow rule are supported by
151  * the device. Currently, only actions that change priorities are supported.
152  * If unsupported actions are encountered, an error message is set in the
153  * extended ACK.
154  *
155  * Returns: 0 on success or a negative error code on failure.
156  */
157 static int ksz9477_flower_parse_action(struct ksz_device *dev, int port,
158 				       struct netlink_ext_ack *extack,
159 				       struct flow_cls_offload *cls,
160 				       int entry_idx)
161 {
162 	struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
163 	struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
164 	const struct flow_action_entry *act;
165 	struct ksz9477_acl_entry *entry;
166 	bool prio_force = false;
167 	u8 prio_val = 0;
168 	int i;
169 
170 	if (TC_H_MIN(cls->classid)) {
171 		NL_SET_ERR_MSG_MOD(extack, "hw_tc is not supported. Use: action skbedit prio");
172 		return -EOPNOTSUPP;
173 	}
174 
175 	flow_action_for_each(i, act, &rule->action) {
176 		switch (act->id) {
177 		case FLOW_ACTION_PRIORITY:
178 			if (act->priority > KSZ9477_MAX_TC) {
179 				NL_SET_ERR_MSG_MOD(extack, "Priority value is too high");
180 				return -EOPNOTSUPP;
181 			}
182 			prio_force = true;
183 			prio_val = act->priority;
184 			break;
185 		default:
186 			NL_SET_ERR_MSG_MOD(extack, "action not supported");
187 			return -EOPNOTSUPP;
188 		}
189 	}
190 
191 	/* pick entry to store action */
192 	entry = &acl->acles.entries[entry_idx];
193 
194 	ksz9477_acl_action_rule_cfg(entry->entry, prio_force, prio_val);
195 	ksz9477_acl_processing_rule_set_action(entry->entry, entry_idx);
196 
197 	return 0;
198 }
199 
200 /**
201  * ksz9477_cls_flower_add - Add a flow classification rule for a specified port
202  *			    on a ksz_device.
203  * @ds: The DSA switch instance.
204  * @port: The port number to add the flow classification rule to.
205  * @cls: The flow_cls_offload instance containing the flow rule.
206  * @ingress: A flag indicating if the rule is applied on the ingress path.
207  *
208  * This function adds a flow classification rule for a specified port on a
209  * ksz_device. It checks if the ACL offloading is supported and parses the flow
210  * keys and actions. If the ACL is not supported, it returns an error. If there
211  * are unprocessed entries, it parses the action for the rule.
212  *
213  * Returns: 0 on success or a negative error code on failure.
214  */
215 int ksz9477_cls_flower_add(struct dsa_switch *ds, int port,
216 			   struct flow_cls_offload *cls, bool ingress)
217 {
218 	struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
219 	struct netlink_ext_ack *extack = cls->common.extack;
220 	struct ksz_device *dev = ds->priv;
221 	struct ksz9477_acl_priv *acl;
222 	int action_entry_idx;
223 	int ret;
224 
225 	acl = dev->ports[port].acl_priv;
226 
227 	if (!acl) {
228 		NL_SET_ERR_MSG_MOD(extack, "ACL offloading is not supported");
229 		return -EOPNOTSUPP;
230 	}
231 
232 	/* A complex rule set can take multiple entries. Use first entry
233 	 * to store the action.
234 	 */
235 	action_entry_idx = acl->acles.entries_count;
236 
237 	ret = ksz9477_flower_parse_key(dev, port, extack, rule, cls->cookie,
238 				       cls->common.prio);
239 	if (ret)
240 		return ret;
241 
242 	ret = ksz9477_flower_parse_action(dev, port, extack, cls,
243 					  action_entry_idx);
244 	if (ret)
245 		return ret;
246 
247 	ret = ksz9477_sort_acl_entries(dev, port);
248 	if (ret)
249 		return ret;
250 
251 	return ksz9477_acl_write_list(dev, port);
252 }
253 
254 /**
255  * ksz9477_cls_flower_del - Remove a flow classification rule for a specified
256  *			    port on a ksz_device.
257  * @ds: The DSA switch instance.
258  * @port: The port number to remove the flow classification rule from.
259  * @cls: The flow_cls_offload instance containing the flow rule.
260  * @ingress: A flag indicating if the rule is applied on the ingress path.
261  *
262  * This function removes a flow classification rule for a specified port on a
263  * ksz_device. It checks if the ACL is initialized, and if not, returns an
264  * error. If the ACL is initialized, it removes entries with the specified
265  * cookie and rewrites the ACL list.
266  *
267  * Returns: 0 on success or a negative error code on failure.
268  */
269 int ksz9477_cls_flower_del(struct dsa_switch *ds, int port,
270 			   struct flow_cls_offload *cls, bool ingress)
271 {
272 	unsigned long cookie = cls->cookie;
273 	struct ksz_device *dev = ds->priv;
274 	struct ksz9477_acl_priv *acl;
275 
276 	acl = dev->ports[port].acl_priv;
277 
278 	if (!acl)
279 		return -EOPNOTSUPP;
280 
281 	ksz9477_acl_remove_entries(dev, port, &acl->acles, cookie);
282 
283 	return ksz9477_acl_write_list(dev, port);
284 }
285