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