1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /* Copyright (c) 2019-2022 Marvell International Ltd. All rights reserved */ 3 4 #include <linux/kernel.h> 5 #include <linux/list.h> 6 7 #include "prestera.h" 8 #include "prestera_hw.h" 9 #include "prestera_flow.h" 10 #include "prestera_flower.h" 11 #include "prestera_matchall.h" 12 #include "prestera_span.h" 13 14 static int prestera_mall_prio_check(struct prestera_flow_block *block, 15 struct tc_cls_matchall_offload *f) 16 { 17 u32 flower_prio_min; 18 u32 flower_prio_max; 19 int err; 20 21 err = prestera_flower_prio_get(block, f->common.chain_index, 22 &flower_prio_min, &flower_prio_max); 23 if (err == -ENOENT) 24 /* No flower filters installed on this chain. */ 25 return 0; 26 27 if (err) { 28 NL_SET_ERR_MSG(f->common.extack, "Failed to get flower priorities"); 29 return err; 30 } 31 32 if (f->common.prio <= flower_prio_max && !block->ingress) { 33 NL_SET_ERR_MSG(f->common.extack, "Failed to add in front of existing flower rules"); 34 return -EOPNOTSUPP; 35 } 36 if (f->common.prio >= flower_prio_min && block->ingress) { 37 NL_SET_ERR_MSG(f->common.extack, "Failed to add behind of existing flower rules"); 38 return -EOPNOTSUPP; 39 } 40 41 return 0; 42 } 43 44 int prestera_mall_prio_get(struct prestera_flow_block *block, 45 u32 *prio_min, u32 *prio_max) 46 { 47 if (!block->mall.bound) 48 return -ENOENT; 49 50 *prio_min = block->mall.prio_min; 51 *prio_max = block->mall.prio_max; 52 return 0; 53 } 54 55 static void prestera_mall_prio_update(struct prestera_flow_block *block, 56 struct tc_cls_matchall_offload *f) 57 { 58 block->mall.prio_min = min(block->mall.prio_min, f->common.prio); 59 block->mall.prio_max = max(block->mall.prio_max, f->common.prio); 60 } 61 62 int prestera_mall_replace(struct prestera_flow_block *block, 63 struct tc_cls_matchall_offload *f) 64 { 65 struct prestera_flow_block_binding *binding; 66 __be16 protocol = f->common.protocol; 67 struct flow_action_entry *act; 68 struct prestera_port *port; 69 int err; 70 71 if (!flow_offload_has_one_action(&f->rule->action)) { 72 NL_SET_ERR_MSG(f->common.extack, 73 "Only singular actions are supported"); 74 return -EOPNOTSUPP; 75 } 76 77 act = &f->rule->action.entries[0]; 78 79 if (!prestera_netdev_check(act->dev)) { 80 NL_SET_ERR_MSG(f->common.extack, 81 "Only Marvell Prestera port is supported"); 82 return -EINVAL; 83 } 84 if (!tc_cls_can_offload_and_chain0(act->dev, &f->common)) 85 return -EOPNOTSUPP; 86 if (act->id != FLOW_ACTION_MIRRED) 87 return -EOPNOTSUPP; 88 if (protocol != htons(ETH_P_ALL)) 89 return -EOPNOTSUPP; 90 91 err = prestera_mall_prio_check(block, f); 92 if (err) 93 return err; 94 95 port = netdev_priv(act->dev); 96 97 list_for_each_entry(binding, &block->binding_list, list) { 98 err = prestera_span_rule_add(binding, port, block->ingress); 99 if (err == -EEXIST) 100 return err; 101 if (err) 102 goto rollback; 103 } 104 105 prestera_mall_prio_update(block, f); 106 107 block->mall.bound = true; 108 return 0; 109 110 rollback: 111 list_for_each_entry_continue_reverse(binding, 112 &block->binding_list, list) 113 prestera_span_rule_del(binding, block->ingress); 114 return err; 115 } 116 117 void prestera_mall_destroy(struct prestera_flow_block *block) 118 { 119 struct prestera_flow_block_binding *binding; 120 121 list_for_each_entry(binding, &block->binding_list, list) 122 prestera_span_rule_del(binding, block->ingress); 123 124 block->mall.prio_min = UINT_MAX; 125 block->mall.prio_max = 0; 126 block->mall.bound = false; 127 } 128