xref: /openbmc/linux/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-flower.c (revision e6eff2056b64376910b72e90fb9140500979da0b)
11110318dSIoana Ciornei // SPDX-License-Identifier: GPL-2.0
21110318dSIoana Ciornei /*
31110318dSIoana Ciornei  * DPAA2 Ethernet Switch flower support
41110318dSIoana Ciornei  *
51110318dSIoana Ciornei  * Copyright 2021 NXP
61110318dSIoana Ciornei  *
71110318dSIoana Ciornei  */
81110318dSIoana Ciornei 
91110318dSIoana Ciornei #include "dpaa2-switch.h"
101110318dSIoana Ciornei 
dpaa2_switch_flower_parse_key(struct flow_cls_offload * cls,struct dpsw_acl_key * acl_key)111110318dSIoana Ciornei static int dpaa2_switch_flower_parse_key(struct flow_cls_offload *cls,
121110318dSIoana Ciornei 					 struct dpsw_acl_key *acl_key)
131110318dSIoana Ciornei {
141110318dSIoana Ciornei 	struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
151110318dSIoana Ciornei 	struct flow_dissector *dissector = rule->match.dissector;
161110318dSIoana Ciornei 	struct netlink_ext_ack *extack = cls->common.extack;
171110318dSIoana Ciornei 	struct dpsw_acl_fields *acl_h, *acl_m;
181110318dSIoana Ciornei 
191110318dSIoana Ciornei 	if (dissector->used_keys &
202b3082c6SRatheesh Kannoth 	    ~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
212b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
222b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
232b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) |
242b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
252b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_IP) |
262b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
272b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS))) {
281110318dSIoana Ciornei 		NL_SET_ERR_MSG_MOD(extack,
291110318dSIoana Ciornei 				   "Unsupported keys used");
301110318dSIoana Ciornei 		return -EOPNOTSUPP;
311110318dSIoana Ciornei 	}
321110318dSIoana Ciornei 
331110318dSIoana Ciornei 	acl_h = &acl_key->match;
341110318dSIoana Ciornei 	acl_m = &acl_key->mask;
351110318dSIoana Ciornei 
361110318dSIoana Ciornei 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
371110318dSIoana Ciornei 		struct flow_match_basic match;
381110318dSIoana Ciornei 
391110318dSIoana Ciornei 		flow_rule_match_basic(rule, &match);
401110318dSIoana Ciornei 		acl_h->l3_protocol = match.key->ip_proto;
411110318dSIoana Ciornei 		acl_h->l2_ether_type = be16_to_cpu(match.key->n_proto);
421110318dSIoana Ciornei 		acl_m->l3_protocol = match.mask->ip_proto;
431110318dSIoana Ciornei 		acl_m->l2_ether_type = be16_to_cpu(match.mask->n_proto);
441110318dSIoana Ciornei 	}
451110318dSIoana Ciornei 
461110318dSIoana Ciornei 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
471110318dSIoana Ciornei 		struct flow_match_eth_addrs match;
481110318dSIoana Ciornei 
491110318dSIoana Ciornei 		flow_rule_match_eth_addrs(rule, &match);
501110318dSIoana Ciornei 		ether_addr_copy(acl_h->l2_dest_mac, &match.key->dst[0]);
511110318dSIoana Ciornei 		ether_addr_copy(acl_h->l2_source_mac, &match.key->src[0]);
521110318dSIoana Ciornei 		ether_addr_copy(acl_m->l2_dest_mac, &match.mask->dst[0]);
531110318dSIoana Ciornei 		ether_addr_copy(acl_m->l2_source_mac, &match.mask->src[0]);
541110318dSIoana Ciornei 	}
551110318dSIoana Ciornei 
561110318dSIoana Ciornei 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
571110318dSIoana Ciornei 		struct flow_match_vlan match;
581110318dSIoana Ciornei 
591110318dSIoana Ciornei 		flow_rule_match_vlan(rule, &match);
601110318dSIoana Ciornei 		acl_h->l2_vlan_id = match.key->vlan_id;
611110318dSIoana Ciornei 		acl_h->l2_tpid = be16_to_cpu(match.key->vlan_tpid);
621110318dSIoana Ciornei 		acl_h->l2_pcp_dei = match.key->vlan_priority << 1 |
631110318dSIoana Ciornei 				    match.key->vlan_dei;
641110318dSIoana Ciornei 
651110318dSIoana Ciornei 		acl_m->l2_vlan_id = match.mask->vlan_id;
661110318dSIoana Ciornei 		acl_m->l2_tpid = be16_to_cpu(match.mask->vlan_tpid);
671110318dSIoana Ciornei 		acl_m->l2_pcp_dei = match.mask->vlan_priority << 1 |
681110318dSIoana Ciornei 				    match.mask->vlan_dei;
691110318dSIoana Ciornei 	}
701110318dSIoana Ciornei 
711110318dSIoana Ciornei 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
721110318dSIoana Ciornei 		struct flow_match_ipv4_addrs match;
731110318dSIoana Ciornei 
741110318dSIoana Ciornei 		flow_rule_match_ipv4_addrs(rule, &match);
751110318dSIoana Ciornei 		acl_h->l3_source_ip = be32_to_cpu(match.key->src);
761110318dSIoana Ciornei 		acl_h->l3_dest_ip = be32_to_cpu(match.key->dst);
771110318dSIoana Ciornei 		acl_m->l3_source_ip = be32_to_cpu(match.mask->src);
781110318dSIoana Ciornei 		acl_m->l3_dest_ip = be32_to_cpu(match.mask->dst);
791110318dSIoana Ciornei 	}
801110318dSIoana Ciornei 
811110318dSIoana Ciornei 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
821110318dSIoana Ciornei 		struct flow_match_ports match;
831110318dSIoana Ciornei 
841110318dSIoana Ciornei 		flow_rule_match_ports(rule, &match);
851110318dSIoana Ciornei 		acl_h->l4_source_port = be16_to_cpu(match.key->src);
861110318dSIoana Ciornei 		acl_h->l4_dest_port = be16_to_cpu(match.key->dst);
871110318dSIoana Ciornei 		acl_m->l4_source_port = be16_to_cpu(match.mask->src);
881110318dSIoana Ciornei 		acl_m->l4_dest_port = be16_to_cpu(match.mask->dst);
891110318dSIoana Ciornei 	}
901110318dSIoana Ciornei 
911110318dSIoana Ciornei 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
921110318dSIoana Ciornei 		struct flow_match_ip match;
931110318dSIoana Ciornei 
941110318dSIoana Ciornei 		flow_rule_match_ip(rule, &match);
951110318dSIoana Ciornei 		if (match.mask->ttl != 0) {
961110318dSIoana Ciornei 			NL_SET_ERR_MSG_MOD(extack,
971110318dSIoana Ciornei 					   "Matching on TTL not supported");
981110318dSIoana Ciornei 			return -EOPNOTSUPP;
991110318dSIoana Ciornei 		}
1001110318dSIoana Ciornei 
1011110318dSIoana Ciornei 		if ((match.mask->tos & 0x3) != 0) {
1021110318dSIoana Ciornei 			NL_SET_ERR_MSG_MOD(extack,
1031110318dSIoana Ciornei 					   "Matching on ECN not supported, only DSCP");
1041110318dSIoana Ciornei 			return -EOPNOTSUPP;
1051110318dSIoana Ciornei 		}
1061110318dSIoana Ciornei 
1071110318dSIoana Ciornei 		acl_h->l3_dscp = match.key->tos >> 2;
1081110318dSIoana Ciornei 		acl_m->l3_dscp = match.mask->tos >> 2;
1091110318dSIoana Ciornei 	}
1101110318dSIoana Ciornei 
1111110318dSIoana Ciornei 	return 0;
1121110318dSIoana Ciornei }
1131110318dSIoana Ciornei 
dpaa2_switch_acl_entry_add(struct dpaa2_switch_filter_block * filter_block,struct dpaa2_switch_acl_entry * entry)114adcb7aa3SIoana Ciornei int dpaa2_switch_acl_entry_add(struct dpaa2_switch_filter_block *filter_block,
1151110318dSIoana Ciornei 			       struct dpaa2_switch_acl_entry *entry)
1161110318dSIoana Ciornei {
1171110318dSIoana Ciornei 	struct dpsw_acl_entry_cfg *acl_entry_cfg = &entry->cfg;
118adcb7aa3SIoana Ciornei 	struct ethsw_core *ethsw = filter_block->ethsw;
1191110318dSIoana Ciornei 	struct dpsw_acl_key *acl_key = &entry->key;
1201110318dSIoana Ciornei 	struct device *dev = ethsw->dev;
1211110318dSIoana Ciornei 	u8 *cmd_buff;
1221110318dSIoana Ciornei 	int err;
1231110318dSIoana Ciornei 
1241110318dSIoana Ciornei 	cmd_buff = kzalloc(DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE, GFP_KERNEL);
1251110318dSIoana Ciornei 	if (!cmd_buff)
1261110318dSIoana Ciornei 		return -ENOMEM;
1271110318dSIoana Ciornei 
1281110318dSIoana Ciornei 	dpsw_acl_prepare_entry_cfg(acl_key, cmd_buff);
1291110318dSIoana Ciornei 
1301110318dSIoana Ciornei 	acl_entry_cfg->key_iova = dma_map_single(dev, cmd_buff,
1311110318dSIoana Ciornei 						 DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE,
1321110318dSIoana Ciornei 						 DMA_TO_DEVICE);
1331110318dSIoana Ciornei 	if (unlikely(dma_mapping_error(dev, acl_entry_cfg->key_iova))) {
1341110318dSIoana Ciornei 		dev_err(dev, "DMA mapping failed\n");
1354fad22a1SYuan Can 		kfree(cmd_buff);
1361110318dSIoana Ciornei 		return -EFAULT;
1371110318dSIoana Ciornei 	}
1381110318dSIoana Ciornei 
1391110318dSIoana Ciornei 	err = dpsw_acl_add_entry(ethsw->mc_io, 0, ethsw->dpsw_handle,
140adcb7aa3SIoana Ciornei 				 filter_block->acl_id, acl_entry_cfg);
1411110318dSIoana Ciornei 
142*77e566c8SIoana Ciornei 	dma_unmap_single(dev, acl_entry_cfg->key_iova,
143*77e566c8SIoana Ciornei 			 DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE,
1441110318dSIoana Ciornei 			 DMA_TO_DEVICE);
1451110318dSIoana Ciornei 	if (err) {
1461110318dSIoana Ciornei 		dev_err(dev, "dpsw_acl_add_entry() failed %d\n", err);
1474fad22a1SYuan Can 		kfree(cmd_buff);
1481110318dSIoana Ciornei 		return err;
1491110318dSIoana Ciornei 	}
1501110318dSIoana Ciornei 
1511110318dSIoana Ciornei 	kfree(cmd_buff);
1521110318dSIoana Ciornei 
1531110318dSIoana Ciornei 	return 0;
1541110318dSIoana Ciornei }
1551110318dSIoana Ciornei 
156adcb7aa3SIoana Ciornei static int
dpaa2_switch_acl_entry_remove(struct dpaa2_switch_filter_block * block,struct dpaa2_switch_acl_entry * entry)157adcb7aa3SIoana Ciornei dpaa2_switch_acl_entry_remove(struct dpaa2_switch_filter_block *block,
1581110318dSIoana Ciornei 			      struct dpaa2_switch_acl_entry *entry)
1591110318dSIoana Ciornei {
1601110318dSIoana Ciornei 	struct dpsw_acl_entry_cfg *acl_entry_cfg = &entry->cfg;
1611110318dSIoana Ciornei 	struct dpsw_acl_key *acl_key = &entry->key;
162adcb7aa3SIoana Ciornei 	struct ethsw_core *ethsw = block->ethsw;
1631110318dSIoana Ciornei 	struct device *dev = ethsw->dev;
1641110318dSIoana Ciornei 	u8 *cmd_buff;
1651110318dSIoana Ciornei 	int err;
1661110318dSIoana Ciornei 
1671110318dSIoana Ciornei 	cmd_buff = kzalloc(DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE, GFP_KERNEL);
1681110318dSIoana Ciornei 	if (!cmd_buff)
1691110318dSIoana Ciornei 		return -ENOMEM;
1701110318dSIoana Ciornei 
1711110318dSIoana Ciornei 	dpsw_acl_prepare_entry_cfg(acl_key, cmd_buff);
1721110318dSIoana Ciornei 
1731110318dSIoana Ciornei 	acl_entry_cfg->key_iova = dma_map_single(dev, cmd_buff,
1741110318dSIoana Ciornei 						 DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE,
1751110318dSIoana Ciornei 						 DMA_TO_DEVICE);
1761110318dSIoana Ciornei 	if (unlikely(dma_mapping_error(dev, acl_entry_cfg->key_iova))) {
1771110318dSIoana Ciornei 		dev_err(dev, "DMA mapping failed\n");
1784fad22a1SYuan Can 		kfree(cmd_buff);
1791110318dSIoana Ciornei 		return -EFAULT;
1801110318dSIoana Ciornei 	}
1811110318dSIoana Ciornei 
1821110318dSIoana Ciornei 	err = dpsw_acl_remove_entry(ethsw->mc_io, 0, ethsw->dpsw_handle,
183adcb7aa3SIoana Ciornei 				    block->acl_id, acl_entry_cfg);
1841110318dSIoana Ciornei 
185*77e566c8SIoana Ciornei 	dma_unmap_single(dev, acl_entry_cfg->key_iova,
186*77e566c8SIoana Ciornei 			 DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE, DMA_TO_DEVICE);
1871110318dSIoana Ciornei 	if (err) {
1881110318dSIoana Ciornei 		dev_err(dev, "dpsw_acl_remove_entry() failed %d\n", err);
1894fad22a1SYuan Can 		kfree(cmd_buff);
1901110318dSIoana Ciornei 		return err;
1911110318dSIoana Ciornei 	}
1921110318dSIoana Ciornei 
1931110318dSIoana Ciornei 	kfree(cmd_buff);
1941110318dSIoana Ciornei 
1951110318dSIoana Ciornei 	return 0;
1961110318dSIoana Ciornei }
1971110318dSIoana Ciornei 
1981110318dSIoana Ciornei static int
dpaa2_switch_acl_entry_add_to_list(struct dpaa2_switch_filter_block * block,struct dpaa2_switch_acl_entry * entry)199adcb7aa3SIoana Ciornei dpaa2_switch_acl_entry_add_to_list(struct dpaa2_switch_filter_block *block,
2001110318dSIoana Ciornei 				   struct dpaa2_switch_acl_entry *entry)
2011110318dSIoana Ciornei {
2021110318dSIoana Ciornei 	struct dpaa2_switch_acl_entry *tmp;
2031110318dSIoana Ciornei 	struct list_head *pos, *n;
2041110318dSIoana Ciornei 	int index = 0;
2051110318dSIoana Ciornei 
206adcb7aa3SIoana Ciornei 	if (list_empty(&block->acl_entries)) {
207adcb7aa3SIoana Ciornei 		list_add(&entry->list, &block->acl_entries);
2081110318dSIoana Ciornei 		return index;
2091110318dSIoana Ciornei 	}
2101110318dSIoana Ciornei 
211adcb7aa3SIoana Ciornei 	list_for_each_safe(pos, n, &block->acl_entries) {
2121110318dSIoana Ciornei 		tmp = list_entry(pos, struct dpaa2_switch_acl_entry, list);
2131110318dSIoana Ciornei 		if (entry->prio < tmp->prio)
2141110318dSIoana Ciornei 			break;
2151110318dSIoana Ciornei 		index++;
2161110318dSIoana Ciornei 	}
2171110318dSIoana Ciornei 	list_add(&entry->list, pos->prev);
2181110318dSIoana Ciornei 	return index;
2191110318dSIoana Ciornei }
2201110318dSIoana Ciornei 
2211110318dSIoana Ciornei static struct dpaa2_switch_acl_entry*
dpaa2_switch_acl_entry_get_by_index(struct dpaa2_switch_filter_block * block,int index)222adcb7aa3SIoana Ciornei dpaa2_switch_acl_entry_get_by_index(struct dpaa2_switch_filter_block *block,
2231110318dSIoana Ciornei 				    int index)
2241110318dSIoana Ciornei {
2251110318dSIoana Ciornei 	struct dpaa2_switch_acl_entry *tmp;
2261110318dSIoana Ciornei 	int i = 0;
2271110318dSIoana Ciornei 
228adcb7aa3SIoana Ciornei 	list_for_each_entry(tmp, &block->acl_entries, list) {
2291110318dSIoana Ciornei 		if (i == index)
2301110318dSIoana Ciornei 			return tmp;
2311110318dSIoana Ciornei 		++i;
2321110318dSIoana Ciornei 	}
2331110318dSIoana Ciornei 
2341110318dSIoana Ciornei 	return NULL;
2351110318dSIoana Ciornei }
2361110318dSIoana Ciornei 
2371110318dSIoana Ciornei static int
dpaa2_switch_acl_entry_set_precedence(struct dpaa2_switch_filter_block * block,struct dpaa2_switch_acl_entry * entry,int precedence)238adcb7aa3SIoana Ciornei dpaa2_switch_acl_entry_set_precedence(struct dpaa2_switch_filter_block *block,
2391110318dSIoana Ciornei 				      struct dpaa2_switch_acl_entry *entry,
2401110318dSIoana Ciornei 				      int precedence)
2411110318dSIoana Ciornei {
2421110318dSIoana Ciornei 	int err;
2431110318dSIoana Ciornei 
244adcb7aa3SIoana Ciornei 	err = dpaa2_switch_acl_entry_remove(block, entry);
2451110318dSIoana Ciornei 	if (err)
2461110318dSIoana Ciornei 		return err;
2471110318dSIoana Ciornei 
2481110318dSIoana Ciornei 	entry->cfg.precedence = precedence;
249adcb7aa3SIoana Ciornei 	return dpaa2_switch_acl_entry_add(block, entry);
2501110318dSIoana Ciornei }
2511110318dSIoana Ciornei 
252adcb7aa3SIoana Ciornei static int
dpaa2_switch_acl_tbl_add_entry(struct dpaa2_switch_filter_block * block,struct dpaa2_switch_acl_entry * entry)253adcb7aa3SIoana Ciornei dpaa2_switch_acl_tbl_add_entry(struct dpaa2_switch_filter_block *block,
2541110318dSIoana Ciornei 			       struct dpaa2_switch_acl_entry *entry)
2551110318dSIoana Ciornei {
2561110318dSIoana Ciornei 	struct dpaa2_switch_acl_entry *tmp;
2571110318dSIoana Ciornei 	int index, i, precedence, err;
2581110318dSIoana Ciornei 
2591110318dSIoana Ciornei 	/* Add the new ACL entry to the linked list and get its index */
260adcb7aa3SIoana Ciornei 	index = dpaa2_switch_acl_entry_add_to_list(block, entry);
2611110318dSIoana Ciornei 
2621110318dSIoana Ciornei 	/* Move up in priority the ACL entries to make space
2631110318dSIoana Ciornei 	 * for the new filter.
2641110318dSIoana Ciornei 	 */
265adcb7aa3SIoana Ciornei 	precedence = DPAA2_ETHSW_PORT_MAX_ACL_ENTRIES - block->num_acl_rules - 1;
2661110318dSIoana Ciornei 	for (i = 0; i < index; i++) {
267adcb7aa3SIoana Ciornei 		tmp = dpaa2_switch_acl_entry_get_by_index(block, i);
2681110318dSIoana Ciornei 
269adcb7aa3SIoana Ciornei 		err = dpaa2_switch_acl_entry_set_precedence(block, tmp,
2701110318dSIoana Ciornei 							    precedence);
2711110318dSIoana Ciornei 		if (err)
2721110318dSIoana Ciornei 			return err;
2731110318dSIoana Ciornei 
2741110318dSIoana Ciornei 		precedence++;
2751110318dSIoana Ciornei 	}
2761110318dSIoana Ciornei 
2771110318dSIoana Ciornei 	/* Add the new entry to hardware */
2781110318dSIoana Ciornei 	entry->cfg.precedence = precedence;
279adcb7aa3SIoana Ciornei 	err = dpaa2_switch_acl_entry_add(block, entry);
280adcb7aa3SIoana Ciornei 	block->num_acl_rules++;
2811110318dSIoana Ciornei 
2821110318dSIoana Ciornei 	return err;
2831110318dSIoana Ciornei }
2841110318dSIoana Ciornei 
2851110318dSIoana Ciornei static struct dpaa2_switch_acl_entry *
dpaa2_switch_acl_tbl_find_entry_by_cookie(struct dpaa2_switch_filter_block * block,unsigned long cookie)286adcb7aa3SIoana Ciornei dpaa2_switch_acl_tbl_find_entry_by_cookie(struct dpaa2_switch_filter_block *block,
2871110318dSIoana Ciornei 					  unsigned long cookie)
2881110318dSIoana Ciornei {
2891110318dSIoana Ciornei 	struct dpaa2_switch_acl_entry *tmp, *n;
2901110318dSIoana Ciornei 
291adcb7aa3SIoana Ciornei 	list_for_each_entry_safe(tmp, n, &block->acl_entries, list) {
2921110318dSIoana Ciornei 		if (tmp->cookie == cookie)
2931110318dSIoana Ciornei 			return tmp;
2941110318dSIoana Ciornei 	}
2951110318dSIoana Ciornei 	return NULL;
2961110318dSIoana Ciornei }
2971110318dSIoana Ciornei 
2981110318dSIoana Ciornei static int
dpaa2_switch_acl_entry_get_index(struct dpaa2_switch_filter_block * block,struct dpaa2_switch_acl_entry * entry)299adcb7aa3SIoana Ciornei dpaa2_switch_acl_entry_get_index(struct dpaa2_switch_filter_block *block,
3001110318dSIoana Ciornei 				 struct dpaa2_switch_acl_entry *entry)
3011110318dSIoana Ciornei {
3021110318dSIoana Ciornei 	struct dpaa2_switch_acl_entry *tmp, *n;
3031110318dSIoana Ciornei 	int index = 0;
3041110318dSIoana Ciornei 
305adcb7aa3SIoana Ciornei 	list_for_each_entry_safe(tmp, n, &block->acl_entries, list) {
3061110318dSIoana Ciornei 		if (tmp->cookie == entry->cookie)
3071110318dSIoana Ciornei 			return index;
3081110318dSIoana Ciornei 		index++;
3091110318dSIoana Ciornei 	}
3101110318dSIoana Ciornei 	return -ENOENT;
3111110318dSIoana Ciornei }
3121110318dSIoana Ciornei 
313e0ead825SIoana Ciornei static struct dpaa2_switch_mirror_entry *
dpaa2_switch_mirror_find_entry_by_cookie(struct dpaa2_switch_filter_block * block,unsigned long cookie)314e0ead825SIoana Ciornei dpaa2_switch_mirror_find_entry_by_cookie(struct dpaa2_switch_filter_block *block,
315e0ead825SIoana Ciornei 					 unsigned long cookie)
316e0ead825SIoana Ciornei {
317e0ead825SIoana Ciornei 	struct dpaa2_switch_mirror_entry *tmp, *n;
318e0ead825SIoana Ciornei 
319e0ead825SIoana Ciornei 	list_for_each_entry_safe(tmp, n, &block->mirror_entries, list) {
320e0ead825SIoana Ciornei 		if (tmp->cookie == cookie)
321e0ead825SIoana Ciornei 			return tmp;
322e0ead825SIoana Ciornei 	}
323e0ead825SIoana Ciornei 	return NULL;
324e0ead825SIoana Ciornei }
325e0ead825SIoana Ciornei 
3261110318dSIoana Ciornei static int
dpaa2_switch_acl_tbl_remove_entry(struct dpaa2_switch_filter_block * block,struct dpaa2_switch_acl_entry * entry)327adcb7aa3SIoana Ciornei dpaa2_switch_acl_tbl_remove_entry(struct dpaa2_switch_filter_block *block,
3281110318dSIoana Ciornei 				  struct dpaa2_switch_acl_entry *entry)
3291110318dSIoana Ciornei {
3301110318dSIoana Ciornei 	struct dpaa2_switch_acl_entry *tmp;
3311110318dSIoana Ciornei 	int index, i, precedence, err;
3321110318dSIoana Ciornei 
333adcb7aa3SIoana Ciornei 	index = dpaa2_switch_acl_entry_get_index(block, entry);
3341110318dSIoana Ciornei 
3351110318dSIoana Ciornei 	/* Remove from hardware the ACL entry */
336adcb7aa3SIoana Ciornei 	err = dpaa2_switch_acl_entry_remove(block, entry);
3371110318dSIoana Ciornei 	if (err)
3381110318dSIoana Ciornei 		return err;
3391110318dSIoana Ciornei 
340adcb7aa3SIoana Ciornei 	block->num_acl_rules--;
3411110318dSIoana Ciornei 
3421110318dSIoana Ciornei 	/* Remove it from the list also */
3431110318dSIoana Ciornei 	list_del(&entry->list);
3441110318dSIoana Ciornei 
3451110318dSIoana Ciornei 	/* Move down in priority the entries over the deleted one */
3461110318dSIoana Ciornei 	precedence = entry->cfg.precedence;
3471110318dSIoana Ciornei 	for (i = index - 1; i >= 0; i--) {
348adcb7aa3SIoana Ciornei 		tmp = dpaa2_switch_acl_entry_get_by_index(block, i);
349adcb7aa3SIoana Ciornei 		err = dpaa2_switch_acl_entry_set_precedence(block, tmp,
3501110318dSIoana Ciornei 							    precedence);
3511110318dSIoana Ciornei 		if (err)
3521110318dSIoana Ciornei 			return err;
3531110318dSIoana Ciornei 
3541110318dSIoana Ciornei 		precedence--;
3551110318dSIoana Ciornei 	}
3561110318dSIoana Ciornei 
3571110318dSIoana Ciornei 	kfree(entry);
3581110318dSIoana Ciornei 
3591110318dSIoana Ciornei 	return 0;
3601110318dSIoana Ciornei }
3611110318dSIoana Ciornei 
dpaa2_switch_tc_parse_action_acl(struct ethsw_core * ethsw,struct flow_action_entry * cls_act,struct dpsw_acl_result * dpsw_act,struct netlink_ext_ack * extack)3623b5d8b44SIoana Ciornei static int dpaa2_switch_tc_parse_action_acl(struct ethsw_core *ethsw,
3631110318dSIoana Ciornei 					    struct flow_action_entry *cls_act,
3641110318dSIoana Ciornei 					    struct dpsw_acl_result *dpsw_act,
3651110318dSIoana Ciornei 					    struct netlink_ext_ack *extack)
3661110318dSIoana Ciornei {
3671110318dSIoana Ciornei 	int err = 0;
3681110318dSIoana Ciornei 
3691110318dSIoana Ciornei 	switch (cls_act->id) {
3701110318dSIoana Ciornei 	case FLOW_ACTION_TRAP:
3711110318dSIoana Ciornei 		dpsw_act->action = DPSW_ACL_ACTION_REDIRECT_TO_CTRL_IF;
3721110318dSIoana Ciornei 		break;
3731110318dSIoana Ciornei 	case FLOW_ACTION_REDIRECT:
3741110318dSIoana Ciornei 		if (!dpaa2_switch_port_dev_check(cls_act->dev)) {
3751110318dSIoana Ciornei 			NL_SET_ERR_MSG_MOD(extack,
3761110318dSIoana Ciornei 					   "Destination not a DPAA2 switch port");
3771110318dSIoana Ciornei 			return -EOPNOTSUPP;
3781110318dSIoana Ciornei 		}
3791110318dSIoana Ciornei 
3801110318dSIoana Ciornei 		dpsw_act->if_id = dpaa2_switch_get_index(ethsw, cls_act->dev);
3811110318dSIoana Ciornei 		dpsw_act->action = DPSW_ACL_ACTION_REDIRECT;
3821110318dSIoana Ciornei 		break;
3831110318dSIoana Ciornei 	case FLOW_ACTION_DROP:
3841110318dSIoana Ciornei 		dpsw_act->action = DPSW_ACL_ACTION_DROP;
3851110318dSIoana Ciornei 		break;
3861110318dSIoana Ciornei 	default:
3871110318dSIoana Ciornei 		NL_SET_ERR_MSG_MOD(extack,
3881110318dSIoana Ciornei 				   "Action not supported");
3891110318dSIoana Ciornei 		err = -EOPNOTSUPP;
3901110318dSIoana Ciornei 		goto out;
3911110318dSIoana Ciornei 	}
3921110318dSIoana Ciornei 
3931110318dSIoana Ciornei out:
3941110318dSIoana Ciornei 	return err;
3951110318dSIoana Ciornei }
3961110318dSIoana Ciornei 
397c5f6d490SIoana Ciornei static int
dpaa2_switch_block_add_mirror(struct dpaa2_switch_filter_block * block,struct dpaa2_switch_mirror_entry * entry,u16 to,struct netlink_ext_ack * extack)398e0ead825SIoana Ciornei dpaa2_switch_block_add_mirror(struct dpaa2_switch_filter_block *block,
399e0ead825SIoana Ciornei 			      struct dpaa2_switch_mirror_entry *entry,
400e0ead825SIoana Ciornei 			      u16 to, struct netlink_ext_ack *extack)
401e0ead825SIoana Ciornei {
402e0ead825SIoana Ciornei 	unsigned long block_ports = block->ports;
403e0ead825SIoana Ciornei 	struct ethsw_core *ethsw = block->ethsw;
4040f3faeceSIoana Ciornei 	struct ethsw_port_priv *port_priv;
405e0ead825SIoana Ciornei 	unsigned long ports_added = 0;
4060f3faeceSIoana Ciornei 	u16 vlan = entry->cfg.vlan_id;
407e0ead825SIoana Ciornei 	bool mirror_port_enabled;
408e0ead825SIoana Ciornei 	int err, port;
409e0ead825SIoana Ciornei 
410e0ead825SIoana Ciornei 	/* Setup the mirroring port */
411e0ead825SIoana Ciornei 	mirror_port_enabled = (ethsw->mirror_port != ethsw->sw_attr.num_ifs);
412e0ead825SIoana Ciornei 	if (!mirror_port_enabled) {
413e0ead825SIoana Ciornei 		err = dpsw_set_reflection_if(ethsw->mc_io, 0,
414e0ead825SIoana Ciornei 					     ethsw->dpsw_handle, to);
415e0ead825SIoana Ciornei 		if (err)
416e0ead825SIoana Ciornei 			return err;
417e0ead825SIoana Ciornei 		ethsw->mirror_port = to;
418e0ead825SIoana Ciornei 	}
419e0ead825SIoana Ciornei 
420e0ead825SIoana Ciornei 	/* Setup the same egress mirroring configuration on all the switch
421e0ead825SIoana Ciornei 	 * ports that share the same filter block.
422e0ead825SIoana Ciornei 	 */
423e0ead825SIoana Ciornei 	for_each_set_bit(port, &block_ports, ethsw->sw_attr.num_ifs) {
4240f3faeceSIoana Ciornei 		port_priv = ethsw->ports[port];
4250f3faeceSIoana Ciornei 
4260f3faeceSIoana Ciornei 		/* We cannot add a per VLAN mirroring rule if the VLAN in
4270f3faeceSIoana Ciornei 		 * question is not installed on the switch port.
4280f3faeceSIoana Ciornei 		 */
4290f3faeceSIoana Ciornei 		if (entry->cfg.filter == DPSW_REFLECTION_FILTER_INGRESS_VLAN &&
4300f3faeceSIoana Ciornei 		    !(port_priv->vlans[vlan] & ETHSW_VLAN_MEMBER)) {
4310f3faeceSIoana Ciornei 			NL_SET_ERR_MSG(extack,
4320f3faeceSIoana Ciornei 				       "VLAN must be installed on the switch port");
4330f3faeceSIoana Ciornei 			err = -EINVAL;
4340f3faeceSIoana Ciornei 			goto err_remove_filters;
4350f3faeceSIoana Ciornei 		}
4360f3faeceSIoana Ciornei 
437e0ead825SIoana Ciornei 		err = dpsw_if_add_reflection(ethsw->mc_io, 0,
438e0ead825SIoana Ciornei 					     ethsw->dpsw_handle,
439e0ead825SIoana Ciornei 					     port, &entry->cfg);
440e0ead825SIoana Ciornei 		if (err)
441e0ead825SIoana Ciornei 			goto err_remove_filters;
442e0ead825SIoana Ciornei 
443e0ead825SIoana Ciornei 		ports_added |= BIT(port);
444e0ead825SIoana Ciornei 	}
445e0ead825SIoana Ciornei 
446e0ead825SIoana Ciornei 	list_add(&entry->list, &block->mirror_entries);
447e0ead825SIoana Ciornei 
448e0ead825SIoana Ciornei 	return 0;
449e0ead825SIoana Ciornei 
450e0ead825SIoana Ciornei err_remove_filters:
451e0ead825SIoana Ciornei 	for_each_set_bit(port, &ports_added, ethsw->sw_attr.num_ifs) {
452e0ead825SIoana Ciornei 		dpsw_if_remove_reflection(ethsw->mc_io, 0, ethsw->dpsw_handle,
453e0ead825SIoana Ciornei 					  port, &entry->cfg);
454e0ead825SIoana Ciornei 	}
455e0ead825SIoana Ciornei 
456e0ead825SIoana Ciornei 	if (!mirror_port_enabled)
457e0ead825SIoana Ciornei 		ethsw->mirror_port = ethsw->sw_attr.num_ifs;
458e0ead825SIoana Ciornei 
459e0ead825SIoana Ciornei 	return err;
460e0ead825SIoana Ciornei }
461e0ead825SIoana Ciornei 
462e0ead825SIoana Ciornei static int
dpaa2_switch_block_remove_mirror(struct dpaa2_switch_filter_block * block,struct dpaa2_switch_mirror_entry * entry)463e0ead825SIoana Ciornei dpaa2_switch_block_remove_mirror(struct dpaa2_switch_filter_block *block,
464e0ead825SIoana Ciornei 				 struct dpaa2_switch_mirror_entry *entry)
465e0ead825SIoana Ciornei {
466e0ead825SIoana Ciornei 	struct dpsw_reflection_cfg *cfg = &entry->cfg;
467e0ead825SIoana Ciornei 	unsigned long block_ports = block->ports;
468e0ead825SIoana Ciornei 	struct ethsw_core *ethsw = block->ethsw;
469e0ead825SIoana Ciornei 	int port;
470e0ead825SIoana Ciornei 
471e0ead825SIoana Ciornei 	/* Remove this mirroring configuration from all the ports belonging to
472e0ead825SIoana Ciornei 	 * the filter block.
473e0ead825SIoana Ciornei 	 */
474e0ead825SIoana Ciornei 	for_each_set_bit(port, &block_ports, ethsw->sw_attr.num_ifs)
475e0ead825SIoana Ciornei 		dpsw_if_remove_reflection(ethsw->mc_io, 0, ethsw->dpsw_handle,
476e0ead825SIoana Ciornei 					  port, cfg);
477e0ead825SIoana Ciornei 
478e0ead825SIoana Ciornei 	/* Also remove it from the list of mirror filters */
479e0ead825SIoana Ciornei 	list_del(&entry->list);
480e0ead825SIoana Ciornei 	kfree(entry);
481e0ead825SIoana Ciornei 
482e0ead825SIoana Ciornei 	/* If this was the last mirror filter, then unset the mirror port */
483e0ead825SIoana Ciornei 	if (list_empty(&block->mirror_entries))
484e0ead825SIoana Ciornei 		ethsw->mirror_port =  ethsw->sw_attr.num_ifs;
485e0ead825SIoana Ciornei 
486e0ead825SIoana Ciornei 	return 0;
487e0ead825SIoana Ciornei }
488e0ead825SIoana Ciornei 
489e0ead825SIoana Ciornei static int
dpaa2_switch_cls_flower_replace_acl(struct dpaa2_switch_filter_block * block,struct flow_cls_offload * cls)490c5f6d490SIoana Ciornei dpaa2_switch_cls_flower_replace_acl(struct dpaa2_switch_filter_block *block,
4911110318dSIoana Ciornei 				    struct flow_cls_offload *cls)
4921110318dSIoana Ciornei {
4931110318dSIoana Ciornei 	struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
4941110318dSIoana Ciornei 	struct netlink_ext_ack *extack = cls->common.extack;
4951110318dSIoana Ciornei 	struct dpaa2_switch_acl_entry *acl_entry;
496adcb7aa3SIoana Ciornei 	struct ethsw_core *ethsw = block->ethsw;
4971110318dSIoana Ciornei 	struct flow_action_entry *act;
4981110318dSIoana Ciornei 	int err;
4991110318dSIoana Ciornei 
500adcb7aa3SIoana Ciornei 	if (dpaa2_switch_acl_tbl_is_full(block)) {
5011110318dSIoana Ciornei 		NL_SET_ERR_MSG(extack, "Maximum filter capacity reached");
5021110318dSIoana Ciornei 		return -ENOMEM;
5031110318dSIoana Ciornei 	}
5041110318dSIoana Ciornei 
5051110318dSIoana Ciornei 	acl_entry = kzalloc(sizeof(*acl_entry), GFP_KERNEL);
5061110318dSIoana Ciornei 	if (!acl_entry)
5071110318dSIoana Ciornei 		return -ENOMEM;
5081110318dSIoana Ciornei 
5091110318dSIoana Ciornei 	err = dpaa2_switch_flower_parse_key(cls, &acl_entry->key);
5101110318dSIoana Ciornei 	if (err)
5111110318dSIoana Ciornei 		goto free_acl_entry;
5121110318dSIoana Ciornei 
5131110318dSIoana Ciornei 	act = &rule->action.entries[0];
5143b5d8b44SIoana Ciornei 	err = dpaa2_switch_tc_parse_action_acl(ethsw, act,
5151110318dSIoana Ciornei 					       &acl_entry->cfg.result, extack);
5161110318dSIoana Ciornei 	if (err)
5171110318dSIoana Ciornei 		goto free_acl_entry;
5181110318dSIoana Ciornei 
5191110318dSIoana Ciornei 	acl_entry->prio = cls->common.prio;
5201110318dSIoana Ciornei 	acl_entry->cookie = cls->cookie;
5211110318dSIoana Ciornei 
522adcb7aa3SIoana Ciornei 	err = dpaa2_switch_acl_tbl_add_entry(block, acl_entry);
5231110318dSIoana Ciornei 	if (err)
5241110318dSIoana Ciornei 		goto free_acl_entry;
5251110318dSIoana Ciornei 
5261110318dSIoana Ciornei 	return 0;
5271110318dSIoana Ciornei 
5281110318dSIoana Ciornei free_acl_entry:
5291110318dSIoana Ciornei 	kfree(acl_entry);
5301110318dSIoana Ciornei 
5311110318dSIoana Ciornei 	return err;
5321110318dSIoana Ciornei }
5331110318dSIoana Ciornei 
dpaa2_switch_flower_parse_mirror_key(struct flow_cls_offload * cls,u16 * vlan)5340f3faeceSIoana Ciornei static int dpaa2_switch_flower_parse_mirror_key(struct flow_cls_offload *cls,
5350f3faeceSIoana Ciornei 						u16 *vlan)
5360f3faeceSIoana Ciornei {
5370f3faeceSIoana Ciornei 	struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
5380f3faeceSIoana Ciornei 	struct flow_dissector *dissector = rule->match.dissector;
5390f3faeceSIoana Ciornei 	struct netlink_ext_ack *extack = cls->common.extack;
5402a36ed7cSTom Rix 	int ret = -EOPNOTSUPP;
5410f3faeceSIoana Ciornei 
5420f3faeceSIoana Ciornei 	if (dissector->used_keys &
5432b3082c6SRatheesh Kannoth 	    ~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
5442b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
5452b3082c6SRatheesh Kannoth 	      BIT_ULL(FLOW_DISSECTOR_KEY_VLAN))) {
5460f3faeceSIoana Ciornei 		NL_SET_ERR_MSG_MOD(extack,
5470f3faeceSIoana Ciornei 				   "Mirroring is supported only per VLAN");
5480f3faeceSIoana Ciornei 		return -EOPNOTSUPP;
5490f3faeceSIoana Ciornei 	}
5500f3faeceSIoana Ciornei 
5510f3faeceSIoana Ciornei 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
5520f3faeceSIoana Ciornei 		struct flow_match_vlan match;
5530f3faeceSIoana Ciornei 
5540f3faeceSIoana Ciornei 		flow_rule_match_vlan(rule, &match);
5550f3faeceSIoana Ciornei 
5560f3faeceSIoana Ciornei 		if (match.mask->vlan_priority != 0 ||
5570f3faeceSIoana Ciornei 		    match.mask->vlan_dei != 0) {
5580f3faeceSIoana Ciornei 			NL_SET_ERR_MSG_MOD(extack,
5590f3faeceSIoana Ciornei 					   "Only matching on VLAN ID supported");
5600f3faeceSIoana Ciornei 			return -EOPNOTSUPP;
5610f3faeceSIoana Ciornei 		}
5620f3faeceSIoana Ciornei 
5630f3faeceSIoana Ciornei 		if (match.mask->vlan_id != 0xFFF) {
5640f3faeceSIoana Ciornei 			NL_SET_ERR_MSG_MOD(extack,
5650f3faeceSIoana Ciornei 					   "Masked matching not supported");
5660f3faeceSIoana Ciornei 			return -EOPNOTSUPP;
5670f3faeceSIoana Ciornei 		}
5680f3faeceSIoana Ciornei 
5690f3faeceSIoana Ciornei 		*vlan = (u16)match.key->vlan_id;
5702a36ed7cSTom Rix 		ret = 0;
5710f3faeceSIoana Ciornei 	}
5720f3faeceSIoana Ciornei 
5732a36ed7cSTom Rix 	return ret;
5740f3faeceSIoana Ciornei }
5750f3faeceSIoana Ciornei 
5760f3faeceSIoana Ciornei static int
dpaa2_switch_cls_flower_replace_mirror(struct dpaa2_switch_filter_block * block,struct flow_cls_offload * cls)5770f3faeceSIoana Ciornei dpaa2_switch_cls_flower_replace_mirror(struct dpaa2_switch_filter_block *block,
5780f3faeceSIoana Ciornei 				       struct flow_cls_offload *cls)
5790f3faeceSIoana Ciornei {
5800f3faeceSIoana Ciornei 	struct netlink_ext_ack *extack = cls->common.extack;
5810f3faeceSIoana Ciornei 	struct dpaa2_switch_mirror_entry *mirror_entry;
5820f3faeceSIoana Ciornei 	struct ethsw_core *ethsw = block->ethsw;
5830f3faeceSIoana Ciornei 	struct dpaa2_switch_mirror_entry *tmp;
5840f3faeceSIoana Ciornei 	struct flow_action_entry *cls_act;
5850f3faeceSIoana Ciornei 	struct list_head *pos, *n;
5860f3faeceSIoana Ciornei 	bool mirror_port_enabled;
5870f3faeceSIoana Ciornei 	u16 if_id, vlan;
5880f3faeceSIoana Ciornei 	int err;
5890f3faeceSIoana Ciornei 
5900f3faeceSIoana Ciornei 	mirror_port_enabled = (ethsw->mirror_port != ethsw->sw_attr.num_ifs);
5910f3faeceSIoana Ciornei 	cls_act = &cls->rule->action.entries[0];
5920f3faeceSIoana Ciornei 
5930f3faeceSIoana Ciornei 	/* Offload rules only when the destination is a DPAA2 switch port */
5940f3faeceSIoana Ciornei 	if (!dpaa2_switch_port_dev_check(cls_act->dev)) {
5950f3faeceSIoana Ciornei 		NL_SET_ERR_MSG_MOD(extack,
5960f3faeceSIoana Ciornei 				   "Destination not a DPAA2 switch port");
5970f3faeceSIoana Ciornei 		return -EOPNOTSUPP;
5980f3faeceSIoana Ciornei 	}
5990f3faeceSIoana Ciornei 	if_id = dpaa2_switch_get_index(ethsw, cls_act->dev);
6000f3faeceSIoana Ciornei 
6010f3faeceSIoana Ciornei 	/* We have a single mirror port but can configure egress mirroring on
6020f3faeceSIoana Ciornei 	 * all the other switch ports. We need to allow mirroring rules only
6030f3faeceSIoana Ciornei 	 * when the destination port is the same.
6040f3faeceSIoana Ciornei 	 */
6050f3faeceSIoana Ciornei 	if (mirror_port_enabled && ethsw->mirror_port != if_id) {
6060f3faeceSIoana Ciornei 		NL_SET_ERR_MSG_MOD(extack,
6070f3faeceSIoana Ciornei 				   "Multiple mirror ports not supported");
6080f3faeceSIoana Ciornei 		return -EBUSY;
6090f3faeceSIoana Ciornei 	}
6100f3faeceSIoana Ciornei 
6110f3faeceSIoana Ciornei 	/* Parse the key */
6120f3faeceSIoana Ciornei 	err = dpaa2_switch_flower_parse_mirror_key(cls, &vlan);
6130f3faeceSIoana Ciornei 	if (err)
6140f3faeceSIoana Ciornei 		return err;
6150f3faeceSIoana Ciornei 
6160f3faeceSIoana Ciornei 	/* Make sure that we don't already have a mirror rule with the same
6170f3faeceSIoana Ciornei 	 * configuration.
6180f3faeceSIoana Ciornei 	 */
6190f3faeceSIoana Ciornei 	list_for_each_safe(pos, n, &block->mirror_entries) {
6200f3faeceSIoana Ciornei 		tmp = list_entry(pos, struct dpaa2_switch_mirror_entry, list);
6210f3faeceSIoana Ciornei 
6220f3faeceSIoana Ciornei 		if (tmp->cfg.filter == DPSW_REFLECTION_FILTER_INGRESS_VLAN &&
6230f3faeceSIoana Ciornei 		    tmp->cfg.vlan_id == vlan) {
6240f3faeceSIoana Ciornei 			NL_SET_ERR_MSG_MOD(extack,
6250f3faeceSIoana Ciornei 					   "VLAN mirror filter already installed");
6260f3faeceSIoana Ciornei 			return -EBUSY;
6270f3faeceSIoana Ciornei 		}
6280f3faeceSIoana Ciornei 	}
6290f3faeceSIoana Ciornei 
6300f3faeceSIoana Ciornei 	mirror_entry = kzalloc(sizeof(*mirror_entry), GFP_KERNEL);
6310f3faeceSIoana Ciornei 	if (!mirror_entry)
6320f3faeceSIoana Ciornei 		return -ENOMEM;
6330f3faeceSIoana Ciornei 
6340f3faeceSIoana Ciornei 	mirror_entry->cfg.filter = DPSW_REFLECTION_FILTER_INGRESS_VLAN;
6350f3faeceSIoana Ciornei 	mirror_entry->cfg.vlan_id = vlan;
6360f3faeceSIoana Ciornei 	mirror_entry->cookie = cls->cookie;
6370f3faeceSIoana Ciornei 
6380f3faeceSIoana Ciornei 	return dpaa2_switch_block_add_mirror(block, mirror_entry, if_id,
6390f3faeceSIoana Ciornei 					     extack);
6400f3faeceSIoana Ciornei }
6410f3faeceSIoana Ciornei 
dpaa2_switch_cls_flower_replace(struct dpaa2_switch_filter_block * block,struct flow_cls_offload * cls)642c5f6d490SIoana Ciornei int dpaa2_switch_cls_flower_replace(struct dpaa2_switch_filter_block *block,
643c5f6d490SIoana Ciornei 				    struct flow_cls_offload *cls)
644c5f6d490SIoana Ciornei {
645c5f6d490SIoana Ciornei 	struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
646c5f6d490SIoana Ciornei 	struct netlink_ext_ack *extack = cls->common.extack;
647c5f6d490SIoana Ciornei 	struct flow_action_entry *act;
648c5f6d490SIoana Ciornei 
649c5f6d490SIoana Ciornei 	if (!flow_offload_has_one_action(&rule->action)) {
650c5f6d490SIoana Ciornei 		NL_SET_ERR_MSG(extack, "Only singular actions are supported");
651c5f6d490SIoana Ciornei 		return -EOPNOTSUPP;
652c5f6d490SIoana Ciornei 	}
653c5f6d490SIoana Ciornei 
654c5f6d490SIoana Ciornei 	act = &rule->action.entries[0];
655c5f6d490SIoana Ciornei 	switch (act->id) {
656c5f6d490SIoana Ciornei 	case FLOW_ACTION_REDIRECT:
657c5f6d490SIoana Ciornei 	case FLOW_ACTION_TRAP:
658c5f6d490SIoana Ciornei 	case FLOW_ACTION_DROP:
659c5f6d490SIoana Ciornei 		return dpaa2_switch_cls_flower_replace_acl(block, cls);
6600f3faeceSIoana Ciornei 	case FLOW_ACTION_MIRRED:
6610f3faeceSIoana Ciornei 		return dpaa2_switch_cls_flower_replace_mirror(block, cls);
662c5f6d490SIoana Ciornei 	default:
663c5f6d490SIoana Ciornei 		NL_SET_ERR_MSG_MOD(extack, "Action not supported");
664c5f6d490SIoana Ciornei 		return -EOPNOTSUPP;
665c5f6d490SIoana Ciornei 	}
666c5f6d490SIoana Ciornei }
667c5f6d490SIoana Ciornei 
dpaa2_switch_cls_flower_destroy(struct dpaa2_switch_filter_block * block,struct flow_cls_offload * cls)668adcb7aa3SIoana Ciornei int dpaa2_switch_cls_flower_destroy(struct dpaa2_switch_filter_block *block,
6691110318dSIoana Ciornei 				    struct flow_cls_offload *cls)
6701110318dSIoana Ciornei {
6710f3faeceSIoana Ciornei 	struct dpaa2_switch_mirror_entry *mirror_entry;
6720f3faeceSIoana Ciornei 	struct dpaa2_switch_acl_entry *acl_entry;
6731110318dSIoana Ciornei 
6740f3faeceSIoana Ciornei 	/* If this filter is a an ACL one, remove it */
6750f3faeceSIoana Ciornei 	acl_entry = dpaa2_switch_acl_tbl_find_entry_by_cookie(block,
6760f3faeceSIoana Ciornei 							      cls->cookie);
6770f3faeceSIoana Ciornei 	if (acl_entry)
6780f3faeceSIoana Ciornei 		return dpaa2_switch_acl_tbl_remove_entry(block, acl_entry);
6790f3faeceSIoana Ciornei 
6800f3faeceSIoana Ciornei 	/* If not, then it has to be a mirror */
6810f3faeceSIoana Ciornei 	mirror_entry = dpaa2_switch_mirror_find_entry_by_cookie(block,
6820f3faeceSIoana Ciornei 								cls->cookie);
6830f3faeceSIoana Ciornei 	if (mirror_entry)
6840f3faeceSIoana Ciornei 		return dpaa2_switch_block_remove_mirror(block,
6850f3faeceSIoana Ciornei 							mirror_entry);
6860f3faeceSIoana Ciornei 
6871110318dSIoana Ciornei 	return 0;
6881110318dSIoana Ciornei }
6894ba28c1aSIoana Ciornei 
6903fa5514aSIoana Ciornei static int
dpaa2_switch_cls_matchall_replace_acl(struct dpaa2_switch_filter_block * block,struct tc_cls_matchall_offload * cls)6913fa5514aSIoana Ciornei dpaa2_switch_cls_matchall_replace_acl(struct dpaa2_switch_filter_block *block,
6924ba28c1aSIoana Ciornei 				      struct tc_cls_matchall_offload *cls)
6934ba28c1aSIoana Ciornei {
6944ba28c1aSIoana Ciornei 	struct netlink_ext_ack *extack = cls->common.extack;
695adcb7aa3SIoana Ciornei 	struct ethsw_core *ethsw = block->ethsw;
6964ba28c1aSIoana Ciornei 	struct dpaa2_switch_acl_entry *acl_entry;
6974ba28c1aSIoana Ciornei 	struct flow_action_entry *act;
6984ba28c1aSIoana Ciornei 	int err;
6994ba28c1aSIoana Ciornei 
700adcb7aa3SIoana Ciornei 	if (dpaa2_switch_acl_tbl_is_full(block)) {
7014ba28c1aSIoana Ciornei 		NL_SET_ERR_MSG(extack, "Maximum filter capacity reached");
7024ba28c1aSIoana Ciornei 		return -ENOMEM;
7034ba28c1aSIoana Ciornei 	}
7044ba28c1aSIoana Ciornei 
7054ba28c1aSIoana Ciornei 	acl_entry = kzalloc(sizeof(*acl_entry), GFP_KERNEL);
7064ba28c1aSIoana Ciornei 	if (!acl_entry)
7074ba28c1aSIoana Ciornei 		return -ENOMEM;
7084ba28c1aSIoana Ciornei 
7094ba28c1aSIoana Ciornei 	act = &cls->rule->action.entries[0];
7103b5d8b44SIoana Ciornei 	err = dpaa2_switch_tc_parse_action_acl(ethsw, act,
7114ba28c1aSIoana Ciornei 					       &acl_entry->cfg.result, extack);
7124ba28c1aSIoana Ciornei 	if (err)
7134ba28c1aSIoana Ciornei 		goto free_acl_entry;
7144ba28c1aSIoana Ciornei 
7154ba28c1aSIoana Ciornei 	acl_entry->prio = cls->common.prio;
7164ba28c1aSIoana Ciornei 	acl_entry->cookie = cls->cookie;
7174ba28c1aSIoana Ciornei 
718adcb7aa3SIoana Ciornei 	err = dpaa2_switch_acl_tbl_add_entry(block, acl_entry);
7194ba28c1aSIoana Ciornei 	if (err)
7204ba28c1aSIoana Ciornei 		goto free_acl_entry;
7214ba28c1aSIoana Ciornei 
7224ba28c1aSIoana Ciornei 	return 0;
7234ba28c1aSIoana Ciornei 
7244ba28c1aSIoana Ciornei free_acl_entry:
7254ba28c1aSIoana Ciornei 	kfree(acl_entry);
7264ba28c1aSIoana Ciornei 
7274ba28c1aSIoana Ciornei 	return err;
7284ba28c1aSIoana Ciornei }
7294ba28c1aSIoana Ciornei 
730e0ead825SIoana Ciornei static int
dpaa2_switch_cls_matchall_replace_mirror(struct dpaa2_switch_filter_block * block,struct tc_cls_matchall_offload * cls)731e0ead825SIoana Ciornei dpaa2_switch_cls_matchall_replace_mirror(struct dpaa2_switch_filter_block *block,
732e0ead825SIoana Ciornei 					 struct tc_cls_matchall_offload *cls)
733e0ead825SIoana Ciornei {
734e0ead825SIoana Ciornei 	struct netlink_ext_ack *extack = cls->common.extack;
735e0ead825SIoana Ciornei 	struct dpaa2_switch_mirror_entry *mirror_entry;
736e0ead825SIoana Ciornei 	struct ethsw_core *ethsw = block->ethsw;
737e0ead825SIoana Ciornei 	struct dpaa2_switch_mirror_entry *tmp;
738e0ead825SIoana Ciornei 	struct flow_action_entry *cls_act;
739e0ead825SIoana Ciornei 	struct list_head *pos, *n;
740e0ead825SIoana Ciornei 	bool mirror_port_enabled;
741e0ead825SIoana Ciornei 	u16 if_id;
742e0ead825SIoana Ciornei 
743e0ead825SIoana Ciornei 	mirror_port_enabled = (ethsw->mirror_port != ethsw->sw_attr.num_ifs);
744e0ead825SIoana Ciornei 	cls_act = &cls->rule->action.entries[0];
745e0ead825SIoana Ciornei 
746e0ead825SIoana Ciornei 	/* Offload rules only when the destination is a DPAA2 switch port */
747e0ead825SIoana Ciornei 	if (!dpaa2_switch_port_dev_check(cls_act->dev)) {
748e0ead825SIoana Ciornei 		NL_SET_ERR_MSG_MOD(extack,
749e0ead825SIoana Ciornei 				   "Destination not a DPAA2 switch port");
750e0ead825SIoana Ciornei 		return -EOPNOTSUPP;
751e0ead825SIoana Ciornei 	}
752e0ead825SIoana Ciornei 	if_id = dpaa2_switch_get_index(ethsw, cls_act->dev);
753e0ead825SIoana Ciornei 
754e0ead825SIoana Ciornei 	/* We have a single mirror port but can configure egress mirroring on
755e0ead825SIoana Ciornei 	 * all the other switch ports. We need to allow mirroring rules only
756e0ead825SIoana Ciornei 	 * when the destination port is the same.
757e0ead825SIoana Ciornei 	 */
758e0ead825SIoana Ciornei 	if (mirror_port_enabled && ethsw->mirror_port != if_id) {
759e0ead825SIoana Ciornei 		NL_SET_ERR_MSG_MOD(extack,
760e0ead825SIoana Ciornei 				   "Multiple mirror ports not supported");
761e0ead825SIoana Ciornei 		return -EBUSY;
762e0ead825SIoana Ciornei 	}
763e0ead825SIoana Ciornei 
764e0ead825SIoana Ciornei 	/* Make sure that we don't already have a mirror rule with the same
765e0ead825SIoana Ciornei 	 * configuration. One matchall rule per block is the maximum.
766e0ead825SIoana Ciornei 	 */
767e0ead825SIoana Ciornei 	list_for_each_safe(pos, n, &block->mirror_entries) {
768e0ead825SIoana Ciornei 		tmp = list_entry(pos, struct dpaa2_switch_mirror_entry, list);
769e0ead825SIoana Ciornei 
770e0ead825SIoana Ciornei 		if (tmp->cfg.filter == DPSW_REFLECTION_FILTER_INGRESS_ALL) {
771e0ead825SIoana Ciornei 			NL_SET_ERR_MSG_MOD(extack,
772e0ead825SIoana Ciornei 					   "Matchall mirror filter already installed");
773e0ead825SIoana Ciornei 			return -EBUSY;
774e0ead825SIoana Ciornei 		}
775e0ead825SIoana Ciornei 	}
776e0ead825SIoana Ciornei 
777e0ead825SIoana Ciornei 	mirror_entry = kzalloc(sizeof(*mirror_entry), GFP_KERNEL);
778e0ead825SIoana Ciornei 	if (!mirror_entry)
779e0ead825SIoana Ciornei 		return -ENOMEM;
780e0ead825SIoana Ciornei 
781e0ead825SIoana Ciornei 	mirror_entry->cfg.filter = DPSW_REFLECTION_FILTER_INGRESS_ALL;
782e0ead825SIoana Ciornei 	mirror_entry->cookie = cls->cookie;
783e0ead825SIoana Ciornei 
784e0ead825SIoana Ciornei 	return dpaa2_switch_block_add_mirror(block, mirror_entry, if_id,
785e0ead825SIoana Ciornei 					     extack);
786e0ead825SIoana Ciornei }
787e0ead825SIoana Ciornei 
dpaa2_switch_cls_matchall_replace(struct dpaa2_switch_filter_block * block,struct tc_cls_matchall_offload * cls)7883fa5514aSIoana Ciornei int dpaa2_switch_cls_matchall_replace(struct dpaa2_switch_filter_block *block,
7893fa5514aSIoana Ciornei 				      struct tc_cls_matchall_offload *cls)
7903fa5514aSIoana Ciornei {
7913fa5514aSIoana Ciornei 	struct netlink_ext_ack *extack = cls->common.extack;
7923fa5514aSIoana Ciornei 	struct flow_action_entry *act;
7933fa5514aSIoana Ciornei 
7943fa5514aSIoana Ciornei 	if (!flow_offload_has_one_action(&cls->rule->action)) {
7953fa5514aSIoana Ciornei 		NL_SET_ERR_MSG(extack, "Only singular actions are supported");
7963fa5514aSIoana Ciornei 		return -EOPNOTSUPP;
7973fa5514aSIoana Ciornei 	}
7983fa5514aSIoana Ciornei 
7993fa5514aSIoana Ciornei 	act = &cls->rule->action.entries[0];
8003fa5514aSIoana Ciornei 	switch (act->id) {
8013fa5514aSIoana Ciornei 	case FLOW_ACTION_REDIRECT:
8023fa5514aSIoana Ciornei 	case FLOW_ACTION_TRAP:
8033fa5514aSIoana Ciornei 	case FLOW_ACTION_DROP:
8043fa5514aSIoana Ciornei 		return dpaa2_switch_cls_matchall_replace_acl(block, cls);
805e0ead825SIoana Ciornei 	case FLOW_ACTION_MIRRED:
806e0ead825SIoana Ciornei 		return dpaa2_switch_cls_matchall_replace_mirror(block, cls);
8073fa5514aSIoana Ciornei 	default:
8083fa5514aSIoana Ciornei 		NL_SET_ERR_MSG_MOD(extack, "Action not supported");
8093fa5514aSIoana Ciornei 		return -EOPNOTSUPP;
8103fa5514aSIoana Ciornei 	}
8113fa5514aSIoana Ciornei }
8123fa5514aSIoana Ciornei 
dpaa2_switch_block_offload_mirror(struct dpaa2_switch_filter_block * block,struct ethsw_port_priv * port_priv)8137a91f907SIoana Ciornei int dpaa2_switch_block_offload_mirror(struct dpaa2_switch_filter_block *block,
8147a91f907SIoana Ciornei 				      struct ethsw_port_priv *port_priv)
8157a91f907SIoana Ciornei {
8167a91f907SIoana Ciornei 	struct ethsw_core *ethsw = port_priv->ethsw_data;
8177a91f907SIoana Ciornei 	struct dpaa2_switch_mirror_entry *tmp;
8187a91f907SIoana Ciornei 	int err;
8197a91f907SIoana Ciornei 
8207a91f907SIoana Ciornei 	list_for_each_entry(tmp, &block->mirror_entries, list) {
8217a91f907SIoana Ciornei 		err = dpsw_if_add_reflection(ethsw->mc_io, 0,
8227a91f907SIoana Ciornei 					     ethsw->dpsw_handle,
8237a91f907SIoana Ciornei 					     port_priv->idx, &tmp->cfg);
8247a91f907SIoana Ciornei 		if (err)
8257a91f907SIoana Ciornei 			goto unwind_add;
8267a91f907SIoana Ciornei 	}
8277a91f907SIoana Ciornei 
8287a91f907SIoana Ciornei 	return 0;
8297a91f907SIoana Ciornei 
8307a91f907SIoana Ciornei unwind_add:
8317a91f907SIoana Ciornei 	list_for_each_entry(tmp, &block->mirror_entries, list)
8327a91f907SIoana Ciornei 		dpsw_if_remove_reflection(ethsw->mc_io, 0,
8337a91f907SIoana Ciornei 					  ethsw->dpsw_handle,
8347a91f907SIoana Ciornei 					  port_priv->idx, &tmp->cfg);
8357a91f907SIoana Ciornei 
8367a91f907SIoana Ciornei 	return err;
8377a91f907SIoana Ciornei }
8387a91f907SIoana Ciornei 
dpaa2_switch_block_unoffload_mirror(struct dpaa2_switch_filter_block * block,struct ethsw_port_priv * port_priv)8397a91f907SIoana Ciornei int dpaa2_switch_block_unoffload_mirror(struct dpaa2_switch_filter_block *block,
8407a91f907SIoana Ciornei 					struct ethsw_port_priv *port_priv)
8417a91f907SIoana Ciornei {
8427a91f907SIoana Ciornei 	struct ethsw_core *ethsw = port_priv->ethsw_data;
8437a91f907SIoana Ciornei 	struct dpaa2_switch_mirror_entry *tmp;
8447a91f907SIoana Ciornei 	int err;
8457a91f907SIoana Ciornei 
8467a91f907SIoana Ciornei 	list_for_each_entry(tmp, &block->mirror_entries, list) {
8477a91f907SIoana Ciornei 		err = dpsw_if_remove_reflection(ethsw->mc_io, 0,
8487a91f907SIoana Ciornei 						ethsw->dpsw_handle,
8497a91f907SIoana Ciornei 						port_priv->idx, &tmp->cfg);
8507a91f907SIoana Ciornei 		if (err)
8517a91f907SIoana Ciornei 			goto unwind_remove;
8527a91f907SIoana Ciornei 	}
8537a91f907SIoana Ciornei 
8547a91f907SIoana Ciornei 	return 0;
8557a91f907SIoana Ciornei 
8567a91f907SIoana Ciornei unwind_remove:
8577a91f907SIoana Ciornei 	list_for_each_entry(tmp, &block->mirror_entries, list)
8587a91f907SIoana Ciornei 		dpsw_if_add_reflection(ethsw->mc_io, 0, ethsw->dpsw_handle,
8597a91f907SIoana Ciornei 				       port_priv->idx, &tmp->cfg);
8607a91f907SIoana Ciornei 
8617a91f907SIoana Ciornei 	return err;
8627a91f907SIoana Ciornei }
8637a91f907SIoana Ciornei 
dpaa2_switch_cls_matchall_destroy(struct dpaa2_switch_filter_block * block,struct tc_cls_matchall_offload * cls)864adcb7aa3SIoana Ciornei int dpaa2_switch_cls_matchall_destroy(struct dpaa2_switch_filter_block *block,
8654ba28c1aSIoana Ciornei 				      struct tc_cls_matchall_offload *cls)
8664ba28c1aSIoana Ciornei {
867e0ead825SIoana Ciornei 	struct dpaa2_switch_mirror_entry *mirror_entry;
868e0ead825SIoana Ciornei 	struct dpaa2_switch_acl_entry *acl_entry;
8694ba28c1aSIoana Ciornei 
870e0ead825SIoana Ciornei 	/* If this filter is a an ACL one, remove it */
871e0ead825SIoana Ciornei 	acl_entry = dpaa2_switch_acl_tbl_find_entry_by_cookie(block,
872e0ead825SIoana Ciornei 							      cls->cookie);
873e0ead825SIoana Ciornei 	if (acl_entry)
874e0ead825SIoana Ciornei 		return dpaa2_switch_acl_tbl_remove_entry(block,
875e0ead825SIoana Ciornei 							 acl_entry);
876e0ead825SIoana Ciornei 
877e0ead825SIoana Ciornei 	/* If not, then it has to be a mirror */
878e0ead825SIoana Ciornei 	mirror_entry = dpaa2_switch_mirror_find_entry_by_cookie(block,
879e0ead825SIoana Ciornei 								cls->cookie);
880e0ead825SIoana Ciornei 	if (mirror_entry)
881e0ead825SIoana Ciornei 		return dpaa2_switch_block_remove_mirror(block,
882e0ead825SIoana Ciornei 							mirror_entry);
883e0ead825SIoana Ciornei 
8844ba28c1aSIoana Ciornei 	return 0;
8854ba28c1aSIoana Ciornei }
886