brintos

brintos / linux-shallow public Read only

0
0
Text · 15.0 KiB · 43913d6 Raw
627 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3#include "lan966x_main.h"4#include "vcap_api.h"5#include "vcap_api_client.h"6#include "vcap_tc.h"7 8#define LAN966X_FORCE_UNTAGED	39 10static bool lan966x_tc_is_known_etype(struct vcap_tc_flower_parse_usage *st,11				      u16 etype)12{13	switch (st->admin->vtype) {14	case VCAP_TYPE_IS1:15		switch (etype) {16		case ETH_P_ALL:17		case ETH_P_ARP:18		case ETH_P_IP:19		case ETH_P_IPV6:20			return true;21		}22		break;23	case VCAP_TYPE_IS2:24		switch (etype) {25		case ETH_P_ALL:26		case ETH_P_ARP:27		case ETH_P_IP:28		case ETH_P_IPV6:29		case ETH_P_SNAP:30		case ETH_P_802_2:31			return true;32		}33		break;34	case VCAP_TYPE_ES0:35		return true;36	default:37		NL_SET_ERR_MSG_MOD(st->fco->common.extack,38				   "VCAP type not supported");39		return false;40	}41 42	return false;43}44 45static int46lan966x_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st)47{48	struct netlink_ext_ack *extack = st->fco->common.extack;49	struct flow_match_control match;50	int err = 0;51 52	flow_rule_match_control(st->frule, &match);53	if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {54		if (match.key->flags & FLOW_DIS_IS_FRAGMENT)55			err = vcap_rule_add_key_bit(st->vrule,56						    VCAP_KF_L3_FRAGMENT,57						    VCAP_BIT_1);58		else59			err = vcap_rule_add_key_bit(st->vrule,60						    VCAP_KF_L3_FRAGMENT,61						    VCAP_BIT_0);62		if (err)63			goto bad_frag_out;64	}65 66	if (match.mask->flags & FLOW_DIS_FIRST_FRAG) {67		if (match.key->flags & FLOW_DIS_FIRST_FRAG)68			err = vcap_rule_add_key_bit(st->vrule,69						    VCAP_KF_L3_FRAG_OFS_GT0,70						    VCAP_BIT_0);71		else72			err = vcap_rule_add_key_bit(st->vrule,73						    VCAP_KF_L3_FRAG_OFS_GT0,74						    VCAP_BIT_1);75		if (err)76			goto bad_frag_out;77	}78 79	if (!flow_rule_is_supp_control_flags(FLOW_DIS_IS_FRAGMENT |80					     FLOW_DIS_FIRST_FRAG,81					     match.mask->flags, extack))82		return -EOPNOTSUPP;83 84	st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL);85 86	return err;87 88bad_frag_out:89	NL_SET_ERR_MSG_MOD(extack, "ip_frag parse error");90	return err;91}92 93static int94lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st)95{96	struct flow_match_basic match;97	int err = 0;98 99	flow_rule_match_basic(st->frule, &match);100	if (match.mask->n_proto) {101		st->l3_proto = be16_to_cpu(match.key->n_proto);102		if (!lan966x_tc_is_known_etype(st, st->l3_proto)) {103			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,104						    st->l3_proto, ~0);105			if (err)106				goto out;107		} else if (st->l3_proto == ETH_P_IP) {108			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,109						    VCAP_BIT_1);110			if (err)111				goto out;112		} else if (st->l3_proto == ETH_P_IPV6 &&113			   st->admin->vtype == VCAP_TYPE_IS1) {114			/* Don't set any keys in this case */115		} else if (st->l3_proto == ETH_P_SNAP &&116			   st->admin->vtype == VCAP_TYPE_IS1) {117			err = vcap_rule_add_key_bit(st->vrule,118						    VCAP_KF_ETYPE_LEN_IS,119						    VCAP_BIT_0);120			if (err)121				goto out;122 123			err = vcap_rule_add_key_bit(st->vrule,124						    VCAP_KF_IP_SNAP_IS,125						    VCAP_BIT_1);126			if (err)127				goto out;128		} else if (st->admin->vtype == VCAP_TYPE_IS1) {129			err = vcap_rule_add_key_bit(st->vrule,130						    VCAP_KF_ETYPE_LEN_IS,131						    VCAP_BIT_1);132			if (err)133				goto out;134 135			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,136						    st->l3_proto, ~0);137			if (err)138				goto out;139		}140	}141	if (match.mask->ip_proto) {142		st->l4_proto = match.key->ip_proto;143 144		if (st->l4_proto == IPPROTO_TCP) {145			if (st->admin->vtype == VCAP_TYPE_IS1) {146				err = vcap_rule_add_key_bit(st->vrule,147							    VCAP_KF_TCP_UDP_IS,148							    VCAP_BIT_1);149				if (err)150					goto out;151			}152 153			err = vcap_rule_add_key_bit(st->vrule,154						    VCAP_KF_TCP_IS,155						    VCAP_BIT_1);156			if (err)157				goto out;158		} else if (st->l4_proto == IPPROTO_UDP) {159			if (st->admin->vtype == VCAP_TYPE_IS1) {160				err = vcap_rule_add_key_bit(st->vrule,161							    VCAP_KF_TCP_UDP_IS,162							    VCAP_BIT_1);163				if (err)164					goto out;165			}166 167			err = vcap_rule_add_key_bit(st->vrule,168						    VCAP_KF_TCP_IS,169						    VCAP_BIT_0);170			if (err)171				goto out;172		} else {173			err = vcap_rule_add_key_u32(st->vrule,174						    VCAP_KF_L3_IP_PROTO,175						    st->l4_proto, ~0);176			if (err)177				goto out;178		}179	}180 181	st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_BASIC);182	return err;183out:184	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");185	return err;186}187 188static int189lan966x_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st)190{191	if (st->admin->vtype != VCAP_TYPE_IS1) {192		NL_SET_ERR_MSG_MOD(st->fco->common.extack,193				   "cvlan not supported in this VCAP");194		return -EINVAL;195	}196 197	return vcap_tc_flower_handler_cvlan_usage(st);198}199 200static int201lan966x_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st)202{203	enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;204	enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;205 206	if (st->admin->vtype == VCAP_TYPE_IS1) {207		vid_key = VCAP_KF_8021Q_VID0;208		pcp_key = VCAP_KF_8021Q_PCP0;209	}210 211	return vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);212}213 214static int215(*lan966x_tc_flower_handlers_usage[])(struct vcap_tc_flower_parse_usage *st) = {216	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage,217	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage,218	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage,219	[FLOW_DISSECTOR_KEY_CONTROL] = lan966x_tc_flower_handler_control_usage,220	[FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage,221	[FLOW_DISSECTOR_KEY_BASIC] = lan966x_tc_flower_handler_basic_usage,222	[FLOW_DISSECTOR_KEY_CVLAN] = lan966x_tc_flower_handler_cvlan_usage,223	[FLOW_DISSECTOR_KEY_VLAN] = lan966x_tc_flower_handler_vlan_usage,224	[FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage,225	[FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage,226	[FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage,227};228 229static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f,230					    struct vcap_admin *admin,231					    struct vcap_rule *vrule,232					    u16 *l3_proto)233{234	struct vcap_tc_flower_parse_usage state = {235		.fco = f,236		.vrule = vrule,237		.l3_proto = ETH_P_ALL,238		.admin = admin,239	};240	int err = 0;241 242	state.frule = flow_cls_offload_flow_rule(f);243	for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) {244		if (!flow_rule_match_key(state.frule, i) ||245		    !lan966x_tc_flower_handlers_usage[i])246			continue;247 248		err = lan966x_tc_flower_handlers_usage[i](&state);249		if (err)250			return err;251	}252 253	if (l3_proto)254		*l3_proto = state.l3_proto;255 256	return err;257}258 259static int lan966x_tc_flower_action_check(struct vcap_control *vctrl,260					  struct net_device *dev,261					  struct flow_cls_offload *fco,262					  bool ingress)263{264	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);265	struct flow_action_entry *actent, *last_actent = NULL;266	struct flow_action *act = &rule->action;267	u64 action_mask = 0;268	int idx;269 270	if (!flow_action_has_entries(act)) {271		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");272		return -EINVAL;273	}274 275	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))276		return -EOPNOTSUPP;277 278	flow_action_for_each(idx, actent, act) {279		if (action_mask & BIT(actent->id)) {280			NL_SET_ERR_MSG_MOD(fco->common.extack,281					   "More actions of the same type");282			return -EINVAL;283		}284		action_mask |= BIT(actent->id);285		last_actent = actent; /* Save last action for later check */286	}287 288	/* Check that last action is a goto289	 * The last chain/lookup does not need to have goto action290	 */291	if (last_actent->id == FLOW_ACTION_GOTO) {292		/* Check if the destination chain is in one of the VCAPs */293		if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,294					 last_actent->chain_index)) {295			NL_SET_ERR_MSG_MOD(fco->common.extack,296					   "Invalid goto chain");297			return -EINVAL;298		}299	} else if (!vcap_is_last_chain(vctrl, fco->common.chain_index,300				       ingress)) {301		NL_SET_ERR_MSG_MOD(fco->common.extack,302				   "Last action must be 'goto'");303		return -EINVAL;304	}305 306	/* Catch unsupported combinations of actions */307	if (action_mask & BIT(FLOW_ACTION_TRAP) &&308	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {309		NL_SET_ERR_MSG_MOD(fco->common.extack,310				   "Cannot combine pass and trap action");311		return -EOPNOTSUPP;312	}313 314	return 0;315}316 317/* Add the actionset that is the default for the VCAP type */318static int lan966x_tc_set_actionset(struct vcap_admin *admin,319				    struct vcap_rule *vrule)320{321	enum vcap_actionfield_set aset;322	int err = 0;323 324	switch (admin->vtype) {325	case VCAP_TYPE_IS1:326		aset = VCAP_AFS_S1;327		break;328	case VCAP_TYPE_IS2:329		aset = VCAP_AFS_BASE_TYPE;330		break;331	case VCAP_TYPE_ES0:332		aset = VCAP_AFS_VID;333		break;334	default:335		return -EINVAL;336	}337 338	/* Do not overwrite any current actionset */339	if (vrule->actionset == VCAP_AFS_NO_VALUE)340		err = vcap_set_rule_set_actionset(vrule, aset);341 342	return err;343}344 345static int lan966x_tc_add_rule_link_target(struct vcap_admin *admin,346					   struct vcap_rule *vrule,347					   int target_cid)348{349	int link_val = target_cid % VCAP_CID_LOOKUP_SIZE;350	int err;351 352	if (!link_val)353		return 0;354 355	switch (admin->vtype) {356	case VCAP_TYPE_IS1:357		/* Choose IS1 specific NXT_IDX key (for chaining rules from IS1) */358		err = vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX_SEL,359					    1, ~0);360		if (err)361			return err;362 363		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX,364					     link_val, ~0);365	case VCAP_TYPE_IS2:366		/* Add IS2 specific PAG key (for chaining rules from IS1) */367		return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG,368					     link_val, ~0);369	case VCAP_TYPE_ES0:370		/* Add ES0 specific ISDX key (for chaining rules from IS1) */371		return vcap_rule_add_key_u32(vrule, VCAP_KF_ISDX_CLS,372					     link_val, ~0);373	default:374		break;375	}376	return 0;377}378 379static int lan966x_tc_add_rule_link(struct vcap_control *vctrl,380				    struct vcap_admin *admin,381				    struct vcap_rule *vrule,382				    struct flow_cls_offload *f,383				    int to_cid)384{385	struct vcap_admin *to_admin = vcap_find_admin(vctrl, to_cid);386	int diff, err = 0;387 388	if (!to_admin) {389		NL_SET_ERR_MSG_MOD(f->common.extack,390				   "Unknown destination chain");391		return -EINVAL;392	}393 394	diff = vcap_chain_offset(vctrl, f->common.chain_index, to_cid);395	if (!diff)396		return 0;397 398	/* Between IS1 and IS2 the PAG value is used */399	if (admin->vtype == VCAP_TYPE_IS1 && to_admin->vtype == VCAP_TYPE_IS2) {400		/* This works for IS1->IS2 */401		err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_VAL, diff);402		if (err)403			return err;404 405		err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_OVERRIDE_MASK,406					       0xff);407		if (err)408			return err;409	} else if (admin->vtype == VCAP_TYPE_IS1 &&410		   to_admin->vtype == VCAP_TYPE_ES0) {411		/* This works for IS1->ES0 */412		err = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_ADD_VAL,413					       diff);414		if (err)415			return err;416 417		err = vcap_rule_add_action_bit(vrule, VCAP_AF_ISDX_REPLACE_ENA,418					       VCAP_BIT_1);419		if (err)420			return err;421	} else {422		NL_SET_ERR_MSG_MOD(f->common.extack,423				   "Unsupported chain destination");424		return -EOPNOTSUPP;425	}426 427	return err;428}429 430static int lan966x_tc_add_rule_counter(struct vcap_admin *admin,431				       struct vcap_rule *vrule)432{433	int err = 0;434 435	switch (admin->vtype) {436	case VCAP_TYPE_ES0:437		err = vcap_rule_mod_action_u32(vrule, VCAP_AF_ESDX,438					       vrule->id);439		break;440	default:441		break;442	}443 444	return err;445}446 447static int lan966x_tc_flower_add(struct lan966x_port *port,448				 struct flow_cls_offload *f,449				 struct vcap_admin *admin,450				 bool ingress)451{452	struct flow_action_entry *act;453	u16 l3_proto = ETH_P_ALL;454	struct flow_rule *frule;455	struct vcap_rule *vrule;456	int err, idx;457 458	err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl,459					     port->dev, f, ingress);460	if (err)461		return err;462 463	vrule = vcap_alloc_rule(port->lan966x->vcap_ctrl, port->dev,464				f->common.chain_index, VCAP_USER_TC,465				f->common.prio, 0);466	if (IS_ERR(vrule))467		return PTR_ERR(vrule);468 469	vrule->cookie = f->cookie;470	err = lan966x_tc_flower_use_dissectors(f, admin, vrule, &l3_proto);471	if (err)472		goto out;473 474	err = lan966x_tc_add_rule_link_target(admin, vrule,475					      f->common.chain_index);476	if (err)477		goto out;478 479	frule = flow_cls_offload_flow_rule(f);480 481	flow_action_for_each(idx, act, &frule->action) {482		switch (act->id) {483		case FLOW_ACTION_TRAP:484			if (admin->vtype != VCAP_TYPE_IS2) {485				NL_SET_ERR_MSG_MOD(f->common.extack,486						   "Trap action not supported in this VCAP");487				err = -EOPNOTSUPP;488				goto out;489			}490 491			err = vcap_rule_add_action_bit(vrule,492						       VCAP_AF_CPU_COPY_ENA,493						       VCAP_BIT_1);494			err |= vcap_rule_add_action_u32(vrule,495							VCAP_AF_CPU_QUEUE_NUM,496							0);497			err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,498							LAN966X_PMM_REPLACE);499			if (err)500				goto out;501 502			break;503		case FLOW_ACTION_GOTO:504			err = lan966x_tc_set_actionset(admin, vrule);505			if (err)506				goto out;507 508			err = lan966x_tc_add_rule_link(port->lan966x->vcap_ctrl,509						       admin, vrule,510						       f, act->chain_index);511			if (err)512				goto out;513 514			break;515		case FLOW_ACTION_VLAN_POP:516			if (admin->vtype != VCAP_TYPE_ES0) {517				NL_SET_ERR_MSG_MOD(f->common.extack,518						   "Cannot use vlan pop on non es0");519				err = -EOPNOTSUPP;520				goto out;521			}522 523			/* Force untag */524			err = vcap_rule_add_action_u32(vrule, VCAP_AF_PUSH_OUTER_TAG,525						       LAN966X_FORCE_UNTAGED);526			if (err)527				goto out;528 529			break;530		default:531			NL_SET_ERR_MSG_MOD(f->common.extack,532					   "Unsupported TC action");533			err = -EOPNOTSUPP;534			goto out;535		}536	}537 538	err = lan966x_tc_add_rule_counter(admin, vrule);539	if (err) {540		vcap_set_tc_exterr(f, vrule);541		goto out;542	}543 544	err = vcap_val_rule(vrule, l3_proto);545	if (err) {546		vcap_set_tc_exterr(f, vrule);547		goto out;548	}549 550	err = vcap_add_rule(vrule);551	if (err)552		NL_SET_ERR_MSG_MOD(f->common.extack,553				   "Could not add the filter");554out:555	vcap_free_rule(vrule);556	return err;557}558 559static int lan966x_tc_flower_del(struct lan966x_port *port,560				 struct flow_cls_offload *f,561				 struct vcap_admin *admin)562{563	struct vcap_control *vctrl;564	int err = -ENOENT, rule_id;565 566	vctrl = port->lan966x->vcap_ctrl;567	while (true) {568		rule_id = vcap_lookup_rule_by_cookie(vctrl, f->cookie);569		if (rule_id <= 0)570			break;571 572		err = vcap_del_rule(vctrl, port->dev, rule_id);573		if (err) {574			NL_SET_ERR_MSG_MOD(f->common.extack,575					   "Cannot delete rule");576			break;577		}578	}579 580	return err;581}582 583static int lan966x_tc_flower_stats(struct lan966x_port *port,584				   struct flow_cls_offload *f,585				   struct vcap_admin *admin)586{587	struct vcap_counter count = {};588	int err;589 590	err = vcap_get_rule_count_by_cookie(port->lan966x->vcap_ctrl,591					    &count, f->cookie);592	if (err)593		return err;594 595	flow_stats_update(&f->stats, 0x0, count.value, 0, 0,596			  FLOW_ACTION_HW_STATS_IMMEDIATE);597 598	return err;599}600 601int lan966x_tc_flower(struct lan966x_port *port,602		      struct flow_cls_offload *f,603		      bool ingress)604{605	struct vcap_admin *admin;606 607	admin = vcap_find_admin(port->lan966x->vcap_ctrl,608				f->common.chain_index);609	if (!admin) {610		NL_SET_ERR_MSG_MOD(f->common.extack, "Invalid chain");611		return -EINVAL;612	}613 614	switch (f->command) {615	case FLOW_CLS_REPLACE:616		return lan966x_tc_flower_add(port, f, admin, ingress);617	case FLOW_CLS_DESTROY:618		return lan966x_tc_flower_del(port, f, admin);619	case FLOW_CLS_STATS:620		return lan966x_tc_flower_stats(port, f, admin);621	default:622		return -EOPNOTSUPP;623	}624 625	return 0;626}627