Linux Audio

Check our new training course

Embedded Linux training

Mar 10-20, 2025, special US time zones
Register
Loading...
Note: File does not exist in v5.9.
  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 */
 27static 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
 89not_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 */
110static 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 */
157static 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 */
215int 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 */
269int 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}