brintos

brintos / linux-shallow public Read only

0
0
Text · 27.5 KiB · a057ec3 Raw
1013 lines · c
1// SPDX-License-Identifier: (GPL-2.0 OR MIT)2/* Microsemi Ocelot Switch driver3 * Copyright (c) 2019 Microsemi Corporation4 */5 6#include <net/pkt_cls.h>7#include <net/tc_act/tc_gact.h>8#include <soc/mscc/ocelot_vcap.h>9#include "ocelot_police.h"10#include "ocelot_vcap.h"11 12/* Arbitrarily chosen constants for encoding the VCAP block and lookup number13 * into the chain number. This is UAPI.14 */15#define VCAP_BLOCK			1000016#define VCAP_LOOKUP			100017#define VCAP_IS1_NUM_LOOKUPS		318#define VCAP_IS2_NUM_LOOKUPS		219#define VCAP_IS2_NUM_PAG		25620#define VCAP_IS1_CHAIN(lookup)		\21	(1 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP)22#define VCAP_IS2_CHAIN(lookup, pag)	\23	(2 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP + (pag))24/* PSFP chain and block ID */25#define PSFP_BLOCK_ID			OCELOT_NUM_VCAP_BLOCKS26#define OCELOT_PSFP_CHAIN		(3 * VCAP_BLOCK)27 28static int ocelot_chain_to_block(int chain, bool ingress)29{30	int lookup, pag;31 32	if (!ingress) {33		if (chain == 0)34			return VCAP_ES0;35		return -EOPNOTSUPP;36	}37 38	/* Backwards compatibility with older, single-chain tc-flower39	 * offload support in Ocelot40	 */41	if (chain == 0)42		return VCAP_IS2;43 44	for (lookup = 0; lookup < VCAP_IS1_NUM_LOOKUPS; lookup++)45		if (chain == VCAP_IS1_CHAIN(lookup))46			return VCAP_IS1;47 48	for (lookup = 0; lookup < VCAP_IS2_NUM_LOOKUPS; lookup++)49		for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)50			if (chain == VCAP_IS2_CHAIN(lookup, pag))51				return VCAP_IS2;52 53	if (chain == OCELOT_PSFP_CHAIN)54		return PSFP_BLOCK_ID;55 56	return -EOPNOTSUPP;57}58 59/* Caller must ensure this is a valid IS1 or IS2 chain first,60 * by calling ocelot_chain_to_block.61 */62static int ocelot_chain_to_lookup(int chain)63{64	/* Backwards compatibility with older, single-chain tc-flower65	 * offload support in Ocelot66	 */67	if (chain == 0)68		return 0;69 70	return (chain / VCAP_LOOKUP) % 10;71}72 73/* Caller must ensure this is a valid IS2 chain first,74 * by calling ocelot_chain_to_block.75 */76static int ocelot_chain_to_pag(int chain)77{78	int lookup;79 80	/* Backwards compatibility with older, single-chain tc-flower81	 * offload support in Ocelot82	 */83	if (chain == 0)84		return 0;85 86	lookup = ocelot_chain_to_lookup(chain);87 88	/* calculate PAG value as chain index relative to the first PAG */89	return chain - VCAP_IS2_CHAIN(lookup, 0);90}91 92static bool ocelot_is_goto_target_valid(int goto_target, int chain,93					bool ingress)94{95	int pag;96 97	/* Can't offload GOTO in VCAP ES0 */98	if (!ingress)99		return (goto_target < 0);100 101	/* Non-optional GOTOs */102	if (chain == 0)103		/* VCAP IS1 can be skipped, either partially or completely */104		return (goto_target == VCAP_IS1_CHAIN(0) ||105			goto_target == VCAP_IS1_CHAIN(1) ||106			goto_target == VCAP_IS1_CHAIN(2) ||107			goto_target == VCAP_IS2_CHAIN(0, 0) ||108			goto_target == VCAP_IS2_CHAIN(1, 0) ||109			goto_target == OCELOT_PSFP_CHAIN);110 111	if (chain == VCAP_IS1_CHAIN(0))112		return (goto_target == VCAP_IS1_CHAIN(1));113 114	if (chain == VCAP_IS1_CHAIN(1))115		return (goto_target == VCAP_IS1_CHAIN(2));116 117	/* Lookup 2 of VCAP IS1 can really support non-optional GOTOs,118	 * using a Policy Association Group (PAG) value, which is an 8-bit119	 * value encoding a VCAP IS2 target chain.120	 */121	if (chain == VCAP_IS1_CHAIN(2)) {122		for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)123			if (goto_target == VCAP_IS2_CHAIN(0, pag))124				return true;125 126		return false;127	}128 129	/* Non-optional GOTO from VCAP IS2 lookup 0 to lookup 1.130	 * We cannot change the PAG at this point.131	 */132	for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)133		if (chain == VCAP_IS2_CHAIN(0, pag))134			return (goto_target == VCAP_IS2_CHAIN(1, pag));135 136	/* VCAP IS2 lookup 1 can goto to PSFP block if hardware support */137	for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)138		if (chain == VCAP_IS2_CHAIN(1, pag))139			return (goto_target == OCELOT_PSFP_CHAIN);140 141	return false;142}143 144static struct ocelot_vcap_filter *145ocelot_find_vcap_filter_that_points_at(struct ocelot *ocelot, int chain)146{147	struct ocelot_vcap_filter *filter;148	struct ocelot_vcap_block *block;149	int block_id;150 151	block_id = ocelot_chain_to_block(chain, true);152	if (block_id < 0)153		return NULL;154 155	if (block_id == VCAP_IS2) {156		block = &ocelot->block[VCAP_IS1];157 158		list_for_each_entry(filter, &block->rules, list)159			if (filter->type == OCELOT_VCAP_FILTER_PAG &&160			    filter->goto_target == chain)161				return filter;162	}163 164	list_for_each_entry(filter, &ocelot->dummy_rules, list)165		if (filter->goto_target == chain)166			return filter;167 168	return NULL;169}170 171static int172ocelot_flower_parse_ingress_vlan_modify(struct ocelot *ocelot, int port,173					struct ocelot_vcap_filter *filter,174					const struct flow_action_entry *a,175					struct netlink_ext_ack *extack)176{177	struct ocelot_port *ocelot_port = ocelot->ports[port];178 179	if (filter->goto_target != -1) {180		NL_SET_ERR_MSG_MOD(extack,181				   "Last action must be GOTO");182		return -EOPNOTSUPP;183	}184 185	if (!ocelot_port->vlan_aware) {186		NL_SET_ERR_MSG_MOD(extack,187				   "Can only modify VLAN under VLAN aware bridge");188		return -EOPNOTSUPP;189	}190 191	filter->action.vid_replace_ena = true;192	filter->action.pcp_dei_ena = true;193	filter->action.vid = a->vlan.vid;194	filter->action.pcp = a->vlan.prio;195	filter->type = OCELOT_VCAP_FILTER_OFFLOAD;196 197	return 0;198}199 200static int201ocelot_flower_parse_egress_vlan_modify(struct ocelot_vcap_filter *filter,202				       const struct flow_action_entry *a,203				       struct netlink_ext_ack *extack)204{205	enum ocelot_tag_tpid_sel tpid;206 207	switch (ntohs(a->vlan.proto)) {208	case ETH_P_8021Q:209		tpid = OCELOT_TAG_TPID_SEL_8021Q;210		break;211	case ETH_P_8021AD:212		tpid = OCELOT_TAG_TPID_SEL_8021AD;213		break;214	default:215		NL_SET_ERR_MSG_MOD(extack,216				   "Cannot modify custom TPID");217		return -EOPNOTSUPP;218	}219 220	filter->action.tag_a_tpid_sel = tpid;221	filter->action.push_outer_tag = OCELOT_ES0_TAG;222	filter->action.tag_a_vid_sel = OCELOT_ES0_VID_PLUS_CLASSIFIED_VID;223	filter->action.vid_a_val = a->vlan.vid;224	filter->action.pcp_a_val = a->vlan.prio;225	filter->action.tag_a_pcp_sel = OCELOT_ES0_PCP;226	filter->type = OCELOT_VCAP_FILTER_OFFLOAD;227 228	return 0;229}230 231static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,232				      bool ingress, struct flow_cls_offload *f,233				      struct ocelot_vcap_filter *filter)234{235	const struct flow_action *action = &f->rule->action;236	struct netlink_ext_ack *extack = f->common.extack;237	bool allow_missing_goto_target = false;238	const struct flow_action_entry *a;239	enum ocelot_tag_tpid_sel tpid;240	int i, chain, egress_port;241	u32 pol_ix, pol_max;242	u64 rate;243	int err;244 245	if (!flow_action_basic_hw_stats_check(&f->rule->action,246					      f->common.extack))247		return -EOPNOTSUPP;248 249	chain = f->common.chain_index;250	filter->block_id = ocelot_chain_to_block(chain, ingress);251	if (filter->block_id < 0) {252		NL_SET_ERR_MSG_MOD(extack, "Cannot offload to this chain");253		return -EOPNOTSUPP;254	}255	if (filter->block_id == VCAP_IS1 || filter->block_id == VCAP_IS2)256		filter->lookup = ocelot_chain_to_lookup(chain);257	if (filter->block_id == VCAP_IS2)258		filter->pag = ocelot_chain_to_pag(chain);259 260	filter->goto_target = -1;261	filter->type = OCELOT_VCAP_FILTER_DUMMY;262 263	flow_action_for_each(i, a, action) {264		switch (a->id) {265		case FLOW_ACTION_DROP:266			if (filter->block_id != VCAP_IS2) {267				NL_SET_ERR_MSG_MOD(extack,268						   "Drop action can only be offloaded to VCAP IS2");269				return -EOPNOTSUPP;270			}271			if (filter->goto_target != -1) {272				NL_SET_ERR_MSG_MOD(extack,273						   "Last action must be GOTO");274				return -EOPNOTSUPP;275			}276			filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;277			filter->action.port_mask = 0;278			filter->action.police_ena = true;279			filter->action.pol_ix = OCELOT_POLICER_DISCARD;280			filter->type = OCELOT_VCAP_FILTER_OFFLOAD;281			break;282		case FLOW_ACTION_ACCEPT:283			if (filter->block_id != VCAP_ES0 &&284			    filter->block_id != VCAP_IS1 &&285			    filter->block_id != VCAP_IS2) {286				NL_SET_ERR_MSG_MOD(extack,287						   "Accept action can only be offloaded to VCAP chains");288				return -EOPNOTSUPP;289			}290			if (filter->block_id != VCAP_ES0 &&291			    filter->goto_target != -1) {292				NL_SET_ERR_MSG_MOD(extack,293						   "Last action must be GOTO");294				return -EOPNOTSUPP;295			}296			filter->type = OCELOT_VCAP_FILTER_OFFLOAD;297			break;298		case FLOW_ACTION_TRAP:299			if (filter->block_id != VCAP_IS2 ||300			    filter->lookup != 0) {301				NL_SET_ERR_MSG_MOD(extack,302						   "Trap action can only be offloaded to VCAP IS2 lookup 0");303				return -EOPNOTSUPP;304			}305			if (filter->goto_target != -1) {306				NL_SET_ERR_MSG_MOD(extack,307						   "Last action must be GOTO");308				return -EOPNOTSUPP;309			}310			filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;311			filter->action.port_mask = 0;312			filter->action.cpu_copy_ena = true;313			filter->action.cpu_qu_num = 0;314			filter->type = OCELOT_VCAP_FILTER_OFFLOAD;315			filter->is_trap = true;316			break;317		case FLOW_ACTION_POLICE:318			if (filter->block_id == PSFP_BLOCK_ID) {319				filter->type = OCELOT_PSFP_FILTER_OFFLOAD;320				break;321			}322			if (filter->block_id != VCAP_IS2 ||323			    filter->lookup != 0) {324				NL_SET_ERR_MSG_MOD(extack,325						   "Police action can only be offloaded to VCAP IS2 lookup 0 or PSFP");326				return -EOPNOTSUPP;327			}328			if (filter->goto_target != -1) {329				NL_SET_ERR_MSG_MOD(extack,330						   "Last action must be GOTO");331				return -EOPNOTSUPP;332			}333 334			err = ocelot_policer_validate(action, a, extack);335			if (err)336				return err;337 338			filter->action.police_ena = true;339 340			pol_ix = a->hw_index + ocelot->vcap_pol.base;341			pol_max = ocelot->vcap_pol.max;342 343			if (ocelot->vcap_pol.max2 && pol_ix > pol_max) {344				pol_ix += ocelot->vcap_pol.base2 - pol_max - 1;345				pol_max = ocelot->vcap_pol.max2;346			}347 348			if (pol_ix >= pol_max)349				return -EINVAL;350 351			filter->action.pol_ix = pol_ix;352 353			rate = a->police.rate_bytes_ps;354			filter->action.pol.rate = div_u64(rate, 1000) * 8;355			filter->action.pol.burst = a->police.burst;356			filter->type = OCELOT_VCAP_FILTER_OFFLOAD;357			break;358		case FLOW_ACTION_REDIRECT:359			if (filter->block_id != VCAP_IS2) {360				NL_SET_ERR_MSG_MOD(extack,361						   "Redirect action can only be offloaded to VCAP IS2");362				return -EOPNOTSUPP;363			}364			if (filter->goto_target != -1) {365				NL_SET_ERR_MSG_MOD(extack,366						   "Last action must be GOTO");367				return -EOPNOTSUPP;368			}369			egress_port = ocelot->ops->netdev_to_port(a->dev);370			if (egress_port < 0) {371				NL_SET_ERR_MSG_MOD(extack,372						   "Destination not an ocelot port");373				return -EOPNOTSUPP;374			}375			filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;376			filter->action.port_mask = BIT(egress_port);377			filter->type = OCELOT_VCAP_FILTER_OFFLOAD;378			break;379		case FLOW_ACTION_MIRRED:380			if (filter->block_id != VCAP_IS2) {381				NL_SET_ERR_MSG_MOD(extack,382						   "Mirror action can only be offloaded to VCAP IS2");383				return -EOPNOTSUPP;384			}385			if (filter->goto_target != -1) {386				NL_SET_ERR_MSG_MOD(extack,387						   "Last action must be GOTO");388				return -EOPNOTSUPP;389			}390			egress_port = ocelot->ops->netdev_to_port(a->dev);391			if (egress_port < 0) {392				NL_SET_ERR_MSG_MOD(extack,393						   "Destination not an ocelot port");394				return -EOPNOTSUPP;395			}396			filter->egress_port.value = egress_port;397			filter->action.mirror_ena = true;398			filter->type = OCELOT_VCAP_FILTER_OFFLOAD;399			break;400		case FLOW_ACTION_VLAN_POP:401			if (filter->block_id != VCAP_IS1) {402				NL_SET_ERR_MSG_MOD(extack,403						   "VLAN pop action can only be offloaded to VCAP IS1");404				return -EOPNOTSUPP;405			}406			if (filter->goto_target != -1) {407				NL_SET_ERR_MSG_MOD(extack,408						   "Last action must be GOTO");409				return -EOPNOTSUPP;410			}411			filter->action.vlan_pop_cnt_ena = true;412			filter->action.vlan_pop_cnt++;413			if (filter->action.vlan_pop_cnt > 2) {414				NL_SET_ERR_MSG_MOD(extack,415						   "Cannot pop more than 2 VLAN headers");416				return -EOPNOTSUPP;417			}418			filter->type = OCELOT_VCAP_FILTER_OFFLOAD;419			break;420		case FLOW_ACTION_VLAN_MANGLE:421			if (filter->block_id == VCAP_IS1) {422				err = ocelot_flower_parse_ingress_vlan_modify(ocelot, port,423									      filter, a,424									      extack);425			} else if (filter->block_id == VCAP_ES0) {426				err = ocelot_flower_parse_egress_vlan_modify(filter, a,427									     extack);428			} else {429				NL_SET_ERR_MSG_MOD(extack,430						   "VLAN modify action can only be offloaded to VCAP IS1 or ES0");431				err = -EOPNOTSUPP;432			}433			if (err)434				return err;435			break;436		case FLOW_ACTION_PRIORITY:437			if (filter->block_id != VCAP_IS1) {438				NL_SET_ERR_MSG_MOD(extack,439						   "Priority action can only be offloaded to VCAP IS1");440				return -EOPNOTSUPP;441			}442			if (filter->goto_target != -1) {443				NL_SET_ERR_MSG_MOD(extack,444						   "Last action must be GOTO");445				return -EOPNOTSUPP;446			}447			filter->action.qos_ena = true;448			filter->action.qos_val = a->priority;449			filter->type = OCELOT_VCAP_FILTER_OFFLOAD;450			break;451		case FLOW_ACTION_GOTO:452			filter->goto_target = a->chain_index;453 454			if (filter->block_id == VCAP_IS1 && filter->lookup == 2) {455				int pag = ocelot_chain_to_pag(filter->goto_target);456 457				filter->action.pag_override_mask = 0xff;458				filter->action.pag_val = pag;459				filter->type = OCELOT_VCAP_FILTER_PAG;460			}461			break;462		case FLOW_ACTION_VLAN_PUSH:463			if (filter->block_id != VCAP_ES0) {464				NL_SET_ERR_MSG_MOD(extack,465						   "VLAN push action can only be offloaded to VCAP ES0");466				return -EOPNOTSUPP;467			}468			switch (ntohs(a->vlan.proto)) {469			case ETH_P_8021Q:470				tpid = OCELOT_TAG_TPID_SEL_8021Q;471				break;472			case ETH_P_8021AD:473				tpid = OCELOT_TAG_TPID_SEL_8021AD;474				break;475			default:476				NL_SET_ERR_MSG_MOD(extack,477						   "Cannot push custom TPID");478				return -EOPNOTSUPP;479			}480			filter->action.tag_a_tpid_sel = tpid;481			filter->action.push_outer_tag = OCELOT_ES0_TAG;482			filter->action.tag_a_vid_sel = OCELOT_ES0_VID;483			filter->action.vid_a_val = a->vlan.vid;484			filter->action.pcp_a_val = a->vlan.prio;485			filter->type = OCELOT_VCAP_FILTER_OFFLOAD;486			break;487		case FLOW_ACTION_GATE:488			if (filter->block_id != PSFP_BLOCK_ID) {489				NL_SET_ERR_MSG_MOD(extack,490						   "Gate action can only be offloaded to PSFP chain");491				return -EOPNOTSUPP;492			}493			filter->type = OCELOT_PSFP_FILTER_OFFLOAD;494			break;495		default:496			NL_SET_ERR_MSG_MOD(extack, "Cannot offload action");497			return -EOPNOTSUPP;498		}499	}500 501	if (filter->goto_target == -1) {502		if ((filter->block_id == VCAP_IS2 && filter->lookup == 1) ||503		    chain == 0 || filter->block_id == PSFP_BLOCK_ID) {504			allow_missing_goto_target = true;505		} else {506			NL_SET_ERR_MSG_MOD(extack, "Missing GOTO action");507			return -EOPNOTSUPP;508		}509	}510 511	if (!ocelot_is_goto_target_valid(filter->goto_target, chain, ingress) &&512	    !allow_missing_goto_target) {513		NL_SET_ERR_MSG_MOD(extack, "Cannot offload this GOTO target");514		return -EOPNOTSUPP;515	}516 517	return 0;518}519 520static int ocelot_flower_parse_indev(struct ocelot *ocelot, int port,521				     struct flow_cls_offload *f,522				     struct ocelot_vcap_filter *filter)523{524	struct flow_rule *rule = flow_cls_offload_flow_rule(f);525	const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];526	int key_length = vcap->keys[VCAP_ES0_IGR_PORT].length;527	struct netlink_ext_ack *extack = f->common.extack;528	struct net_device *dev, *indev;529	struct flow_match_meta match;530	int ingress_port;531 532	flow_rule_match_meta(rule, &match);533 534	if (!match.mask->ingress_ifindex)535		return 0;536 537	if (match.mask->ingress_ifindex != 0xFFFFFFFF) {538		NL_SET_ERR_MSG_MOD(extack, "Unsupported ingress ifindex mask");539		return -EOPNOTSUPP;540	}541 542	dev = ocelot->ops->port_to_netdev(ocelot, port);543	if (!dev)544		return -EINVAL;545 546	indev = __dev_get_by_index(dev_net(dev), match.key->ingress_ifindex);547	if (!indev) {548		NL_SET_ERR_MSG_MOD(extack,549				   "Can't find the ingress port to match on");550		return -ENOENT;551	}552 553	ingress_port = ocelot->ops->netdev_to_port(indev);554	if (ingress_port < 0) {555		NL_SET_ERR_MSG_MOD(extack,556				   "Can only offload an ocelot ingress port");557		return -EOPNOTSUPP;558	}559	if (ingress_port == port) {560		NL_SET_ERR_MSG_MOD(extack,561				   "Ingress port is equal to the egress port");562		return -EINVAL;563	}564 565	filter->ingress_port.value = ingress_port;566	filter->ingress_port.mask = GENMASK(key_length - 1, 0);567 568	return 0;569}570 571static int572ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,573			struct flow_cls_offload *f,574			struct ocelot_vcap_filter *filter)575{576	struct flow_rule *rule = flow_cls_offload_flow_rule(f);577	struct flow_dissector *dissector = rule->match.dissector;578	struct netlink_ext_ack *extack = f->common.extack;579	u16 proto = ntohs(f->common.protocol);580	bool match_protocol = true;581	int ret;582 583	if (dissector->used_keys &584	    ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |585	      BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |586	      BIT_ULL(FLOW_DISSECTOR_KEY_META) |587	      BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |588	      BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) |589	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |590	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |591	      BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS))) {592		return -EOPNOTSUPP;593	}594 595	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) {596		struct flow_match_meta match;597 598		flow_rule_match_meta(rule, &match);599		if (match.mask->l2_miss) {600			NL_SET_ERR_MSG_MOD(extack, "Can't match on \"l2_miss\"");601			return -EOPNOTSUPP;602		}603	}604 605	/* For VCAP ES0 (egress rewriter) we can match on the ingress port */606	if (!ingress) {607		ret = ocelot_flower_parse_indev(ocelot, port, f, filter);608		if (ret)609			return ret;610	}611 612	if (flow_rule_match_has_control_flags(rule, extack))613		return -EOPNOTSUPP;614 615	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {616		struct flow_match_vlan match;617 618		flow_rule_match_vlan(rule, &match);619		filter->key_type = OCELOT_VCAP_KEY_ANY;620		filter->vlan.vid.value = match.key->vlan_id;621		filter->vlan.vid.mask = match.mask->vlan_id;622		filter->vlan.pcp.value[0] = match.key->vlan_priority;623		filter->vlan.pcp.mask[0] = match.mask->vlan_priority;624		match_protocol = false;625	}626 627	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {628		struct flow_match_eth_addrs match;629 630		if (filter->block_id == VCAP_ES0) {631			NL_SET_ERR_MSG_MOD(extack,632					   "VCAP ES0 cannot match on MAC address");633			return -EOPNOTSUPP;634		}635 636		/* The hw support mac matches only for MAC_ETYPE key,637		 * therefore if other matches(port, tcp flags, etc) are added638		 * then just bail out639		 */640		if ((dissector->used_keys &641		    (BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |642		     BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |643		     BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL))) !=644		    (BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |645		     BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |646		     BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL)))647			return -EOPNOTSUPP;648 649		flow_rule_match_eth_addrs(rule, &match);650 651		if (filter->block_id == VCAP_IS1 &&652		    !is_zero_ether_addr(match.mask->dst)) {653			NL_SET_ERR_MSG_MOD(extack,654					   "Key type S1_NORMAL cannot match on destination MAC");655			return -EOPNOTSUPP;656		}657 658		filter->key_type = OCELOT_VCAP_KEY_ETYPE;659		ether_addr_copy(filter->key.etype.dmac.value,660				match.key->dst);661		ether_addr_copy(filter->key.etype.smac.value,662				match.key->src);663		ether_addr_copy(filter->key.etype.dmac.mask,664				match.mask->dst);665		ether_addr_copy(filter->key.etype.smac.mask,666				match.mask->src);667		goto finished_key_parsing;668	}669 670	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {671		struct flow_match_basic match;672 673		flow_rule_match_basic(rule, &match);674		if (ntohs(match.key->n_proto) == ETH_P_IP) {675			if (filter->block_id == VCAP_ES0) {676				NL_SET_ERR_MSG_MOD(extack,677						   "VCAP ES0 cannot match on IP protocol");678				return -EOPNOTSUPP;679			}680 681			filter->key_type = OCELOT_VCAP_KEY_IPV4;682			filter->key.ipv4.proto.value[0] =683				match.key->ip_proto;684			filter->key.ipv4.proto.mask[0] =685				match.mask->ip_proto;686			match_protocol = false;687		}688		if (ntohs(match.key->n_proto) == ETH_P_IPV6) {689			if (filter->block_id == VCAP_ES0) {690				NL_SET_ERR_MSG_MOD(extack,691						   "VCAP ES0 cannot match on IP protocol");692				return -EOPNOTSUPP;693			}694 695			filter->key_type = OCELOT_VCAP_KEY_IPV6;696			filter->key.ipv6.proto.value[0] =697				match.key->ip_proto;698			filter->key.ipv6.proto.mask[0] =699				match.mask->ip_proto;700			match_protocol = false;701		}702	}703 704	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&705	    proto == ETH_P_IP) {706		struct flow_match_ipv4_addrs match;707		u8 *tmp;708 709		if (filter->block_id == VCAP_ES0) {710			NL_SET_ERR_MSG_MOD(extack,711					   "VCAP ES0 cannot match on IP address");712			return -EOPNOTSUPP;713		}714 715		flow_rule_match_ipv4_addrs(rule, &match);716 717		if (filter->block_id == VCAP_IS1 && *(u32 *)&match.mask->dst) {718			NL_SET_ERR_MSG_MOD(extack,719					   "Key type S1_NORMAL cannot match on destination IP");720			return -EOPNOTSUPP;721		}722 723		tmp = &filter->key.ipv4.sip.value.addr[0];724		memcpy(tmp, &match.key->src, 4);725 726		tmp = &filter->key.ipv4.sip.mask.addr[0];727		memcpy(tmp, &match.mask->src, 4);728 729		tmp = &filter->key.ipv4.dip.value.addr[0];730		memcpy(tmp, &match.key->dst, 4);731 732		tmp = &filter->key.ipv4.dip.mask.addr[0];733		memcpy(tmp, &match.mask->dst, 4);734		match_protocol = false;735	}736 737	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&738	    proto == ETH_P_IPV6) {739		return -EOPNOTSUPP;740	}741 742	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {743		struct flow_match_ports match;744 745		if (filter->block_id == VCAP_ES0) {746			NL_SET_ERR_MSG_MOD(extack,747					   "VCAP ES0 cannot match on L4 ports");748			return -EOPNOTSUPP;749		}750 751		flow_rule_match_ports(rule, &match);752		filter->key.ipv4.sport.value = ntohs(match.key->src);753		filter->key.ipv4.sport.mask = ntohs(match.mask->src);754		filter->key.ipv4.dport.value = ntohs(match.key->dst);755		filter->key.ipv4.dport.mask = ntohs(match.mask->dst);756		match_protocol = false;757	}758 759finished_key_parsing:760	if (match_protocol && proto != ETH_P_ALL) {761		if (filter->block_id == VCAP_ES0) {762			NL_SET_ERR_MSG_MOD(extack,763					   "VCAP ES0 cannot match on L2 proto");764			return -EOPNOTSUPP;765		}766 767		/* TODO: support SNAP, LLC etc */768		if (proto < ETH_P_802_3_MIN)769			return -EOPNOTSUPP;770		filter->key_type = OCELOT_VCAP_KEY_ETYPE;771		*(__be16 *)filter->key.etype.etype.value = htons(proto);772		*(__be16 *)filter->key.etype.etype.mask = htons(0xffff);773	}774	/* else, a filter of type OCELOT_VCAP_KEY_ANY is implicitly added */775 776	return 0;777}778 779static int ocelot_flower_parse(struct ocelot *ocelot, int port, bool ingress,780			       struct flow_cls_offload *f,781			       struct ocelot_vcap_filter *filter)782{783	int ret;784 785	filter->prio = f->common.prio;786	filter->id.cookie = f->cookie;787	filter->id.tc_offload = true;788 789	ret = ocelot_flower_parse_action(ocelot, port, ingress, f, filter);790	if (ret)791		return ret;792 793	/* PSFP filter need to parse key by stream identification function. */794	if (filter->type == OCELOT_PSFP_FILTER_OFFLOAD)795		return 0;796 797	return ocelot_flower_parse_key(ocelot, port, ingress, f, filter);798}799 800static struct ocelot_vcap_filter801*ocelot_vcap_filter_create(struct ocelot *ocelot, int port, bool ingress,802			   struct flow_cls_offload *f)803{804	struct ocelot_vcap_filter *filter;805 806	filter = kzalloc(sizeof(*filter), GFP_KERNEL);807	if (!filter)808		return NULL;809 810	if (ingress) {811		filter->ingress_port_mask = BIT(port);812	} else {813		const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];814		int key_length = vcap->keys[VCAP_ES0_EGR_PORT].length;815 816		filter->egress_port.value = port;817		filter->egress_port.mask = GENMASK(key_length - 1, 0);818	}819 820	return filter;821}822 823static int ocelot_vcap_dummy_filter_add(struct ocelot *ocelot,824					struct ocelot_vcap_filter *filter)825{826	list_add(&filter->list, &ocelot->dummy_rules);827 828	return 0;829}830 831static int ocelot_vcap_dummy_filter_del(struct ocelot *ocelot,832					struct ocelot_vcap_filter *filter)833{834	list_del(&filter->list);835	kfree(filter);836 837	return 0;838}839 840/* If we have an egress VLAN modification rule, we need to actually write the841 * delta between the input VLAN (from the key) and the output VLAN (from the842 * action), but the action was parsed first. So we need to patch the delta into843 * the action here.844 */845static int846ocelot_flower_patch_es0_vlan_modify(struct ocelot_vcap_filter *filter,847				    struct netlink_ext_ack *extack)848{849	if (filter->block_id != VCAP_ES0 ||850	    filter->action.tag_a_vid_sel != OCELOT_ES0_VID_PLUS_CLASSIFIED_VID)851		return 0;852 853	if (filter->vlan.vid.mask != VLAN_VID_MASK) {854		NL_SET_ERR_MSG_MOD(extack,855				   "VCAP ES0 VLAN rewriting needs a full VLAN in the key");856		return -EOPNOTSUPP;857	}858 859	filter->action.vid_a_val -= filter->vlan.vid.value;860	filter->action.vid_a_val &= VLAN_VID_MASK;861 862	return 0;863}864 865int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,866			      struct flow_cls_offload *f, bool ingress)867{868	struct netlink_ext_ack *extack = f->common.extack;869	struct ocelot_vcap_filter *filter;870	int chain = f->common.chain_index;871	int block_id, ret;872 873	if (chain && !ocelot_find_vcap_filter_that_points_at(ocelot, chain)) {874		NL_SET_ERR_MSG_MOD(extack, "No default GOTO action points to this chain");875		return -EOPNOTSUPP;876	}877 878	block_id = ocelot_chain_to_block(chain, ingress);879	if (block_id < 0) {880		NL_SET_ERR_MSG_MOD(extack, "Cannot offload to this chain");881		return -EOPNOTSUPP;882	}883 884	filter = ocelot_vcap_block_find_filter_by_id(&ocelot->block[block_id],885						     f->cookie, true);886	if (filter) {887		/* Filter already exists on other ports */888		if (!ingress) {889			NL_SET_ERR_MSG_MOD(extack, "VCAP ES0 does not support shared filters");890			return -EOPNOTSUPP;891		}892 893		filter->ingress_port_mask |= BIT(port);894 895		return ocelot_vcap_filter_replace(ocelot, filter);896	}897 898	/* Filter didn't exist, create it now */899	filter = ocelot_vcap_filter_create(ocelot, port, ingress, f);900	if (!filter)901		return -ENOMEM;902 903	ret = ocelot_flower_parse(ocelot, port, ingress, f, filter);904	if (ret) {905		kfree(filter);906		return ret;907	}908 909	ret = ocelot_flower_patch_es0_vlan_modify(filter, extack);910	if (ret) {911		kfree(filter);912		return ret;913	}914 915	/* The non-optional GOTOs for the TCAM skeleton don't need916	 * to be actually offloaded.917	 */918	if (filter->type == OCELOT_VCAP_FILTER_DUMMY)919		return ocelot_vcap_dummy_filter_add(ocelot, filter);920 921	if (filter->type == OCELOT_PSFP_FILTER_OFFLOAD) {922		kfree(filter);923		if (ocelot->ops->psfp_filter_add)924			return ocelot->ops->psfp_filter_add(ocelot, port, f);925 926		NL_SET_ERR_MSG_MOD(extack, "PSFP chain is not supported in HW");927		return -EOPNOTSUPP;928	}929 930	return ocelot_vcap_filter_add(ocelot, filter, f->common.extack);931}932EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);933 934int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,935			      struct flow_cls_offload *f, bool ingress)936{937	struct ocelot_vcap_filter *filter;938	struct ocelot_vcap_block *block;939	int block_id;940 941	block_id = ocelot_chain_to_block(f->common.chain_index, ingress);942	if (block_id < 0)943		return 0;944 945	if (block_id == PSFP_BLOCK_ID) {946		if (ocelot->ops->psfp_filter_del)947			return ocelot->ops->psfp_filter_del(ocelot, f);948 949		return -EOPNOTSUPP;950	}951 952	block = &ocelot->block[block_id];953 954	filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie, true);955	if (!filter)956		return 0;957 958	if (filter->type == OCELOT_VCAP_FILTER_DUMMY)959		return ocelot_vcap_dummy_filter_del(ocelot, filter);960 961	if (ingress) {962		filter->ingress_port_mask &= ~BIT(port);963		if (filter->ingress_port_mask)964			return ocelot_vcap_filter_replace(ocelot, filter);965	}966 967	return ocelot_vcap_filter_del(ocelot, filter);968}969EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);970 971int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,972			    struct flow_cls_offload *f, bool ingress)973{974	struct ocelot_vcap_filter *filter;975	struct ocelot_vcap_block *block;976	struct flow_stats stats = {0};977	int block_id, ret;978 979	block_id = ocelot_chain_to_block(f->common.chain_index, ingress);980	if (block_id < 0)981		return 0;982 983	if (block_id == PSFP_BLOCK_ID) {984		if (ocelot->ops->psfp_stats_get) {985			ret = ocelot->ops->psfp_stats_get(ocelot, f, &stats);986			if (ret)987				return ret;988 989			goto stats_update;990		}991 992		return -EOPNOTSUPP;993	}994 995	block = &ocelot->block[block_id];996 997	filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie, true);998	if (!filter || filter->type == OCELOT_VCAP_FILTER_DUMMY)999		return 0;1000 1001	ret = ocelot_vcap_filter_stats_update(ocelot, filter);1002	if (ret)1003		return ret;1004 1005	stats.pkts = filter->stats.pkts;1006 1007stats_update:1008	flow_stats_update(&f->stats, 0x0, stats.pkts, stats.drops, 0x0,1009			  FLOW_ACTION_HW_STATS_IMMEDIATE);1010	return 0;1011}1012EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats);1013