brintos

brintos / linux-shallow public Read only

0
0
Text · 8.6 KiB · ca7830a Raw
285 lines · c
1// SPDX-License-Identifier: GPL-2.02// 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			710 11/**12 * ksz9477_flower_parse_key_l2 - Parse Layer 2 key from flow rule and configure13 *                               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 configures22 * the corresponding ACL entries. It checks for unsupported offloads and23 * 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 a96 *			      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 by105 * the device and parses the L2 keys if they match. If unsupported keys are106 * 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 port143 *				 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 by151 * the device. Currently, only actions that change priorities are supported.152 * If unsupported actions are encountered, an error message is set in the153 * 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 port202 *			    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 a209 * ksz_device. It checks if the ACL offloading is supported and parses the flow210 * keys and actions. If the ACL is not supported, it returns an error. If there211 * 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 entry233	 * 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 specified256 *			    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 a263 * ksz_device. It checks if the ACL is initialized, and if not, returns an264 * error. If the ACL is initialized, it removes entries with the specified265 * 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}285