162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019-2022 Marvell International Ltd. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/list.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "prestera.h" 862306a36Sopenharmony_ci#include "prestera_hw.h" 962306a36Sopenharmony_ci#include "prestera_flow.h" 1062306a36Sopenharmony_ci#include "prestera_flower.h" 1162306a36Sopenharmony_ci#include "prestera_matchall.h" 1262306a36Sopenharmony_ci#include "prestera_span.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic int prestera_mall_prio_check(struct prestera_flow_block *block, 1562306a36Sopenharmony_ci struct tc_cls_matchall_offload *f) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci u32 flower_prio_min; 1862306a36Sopenharmony_ci u32 flower_prio_max; 1962306a36Sopenharmony_ci int err; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci err = prestera_flower_prio_get(block, f->common.chain_index, 2262306a36Sopenharmony_ci &flower_prio_min, &flower_prio_max); 2362306a36Sopenharmony_ci if (err == -ENOENT) 2462306a36Sopenharmony_ci /* No flower filters installed on this chain. */ 2562306a36Sopenharmony_ci return 0; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (err) { 2862306a36Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "Failed to get flower priorities"); 2962306a36Sopenharmony_ci return err; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (f->common.prio <= flower_prio_max && !block->ingress) { 3362306a36Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "Failed to add in front of existing flower rules"); 3462306a36Sopenharmony_ci return -EOPNOTSUPP; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci if (f->common.prio >= flower_prio_min && block->ingress) { 3762306a36Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "Failed to add behind of existing flower rules"); 3862306a36Sopenharmony_ci return -EOPNOTSUPP; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciint prestera_mall_prio_get(struct prestera_flow_block *block, 4562306a36Sopenharmony_ci u32 *prio_min, u32 *prio_max) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci if (!block->mall.bound) 4862306a36Sopenharmony_ci return -ENOENT; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci *prio_min = block->mall.prio_min; 5162306a36Sopenharmony_ci *prio_max = block->mall.prio_max; 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void prestera_mall_prio_update(struct prestera_flow_block *block, 5662306a36Sopenharmony_ci struct tc_cls_matchall_offload *f) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci block->mall.prio_min = min(block->mall.prio_min, f->common.prio); 5962306a36Sopenharmony_ci block->mall.prio_max = max(block->mall.prio_max, f->common.prio); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciint prestera_mall_replace(struct prestera_flow_block *block, 6362306a36Sopenharmony_ci struct tc_cls_matchall_offload *f) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct prestera_flow_block_binding *binding; 6662306a36Sopenharmony_ci __be16 protocol = f->common.protocol; 6762306a36Sopenharmony_ci struct flow_action_entry *act; 6862306a36Sopenharmony_ci struct prestera_port *port; 6962306a36Sopenharmony_ci int err; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (!flow_offload_has_one_action(&f->rule->action)) { 7262306a36Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, 7362306a36Sopenharmony_ci "Only singular actions are supported"); 7462306a36Sopenharmony_ci return -EOPNOTSUPP; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci act = &f->rule->action.entries[0]; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (!prestera_netdev_check(act->dev)) { 8062306a36Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, 8162306a36Sopenharmony_ci "Only Marvell Prestera port is supported"); 8262306a36Sopenharmony_ci return -EINVAL; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci if (!tc_cls_can_offload_and_chain0(act->dev, &f->common)) 8562306a36Sopenharmony_ci return -EOPNOTSUPP; 8662306a36Sopenharmony_ci if (act->id != FLOW_ACTION_MIRRED) 8762306a36Sopenharmony_ci return -EOPNOTSUPP; 8862306a36Sopenharmony_ci if (protocol != htons(ETH_P_ALL)) 8962306a36Sopenharmony_ci return -EOPNOTSUPP; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci err = prestera_mall_prio_check(block, f); 9262306a36Sopenharmony_ci if (err) 9362306a36Sopenharmony_ci return err; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci port = netdev_priv(act->dev); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci list_for_each_entry(binding, &block->binding_list, list) { 9862306a36Sopenharmony_ci err = prestera_span_rule_add(binding, port, block->ingress); 9962306a36Sopenharmony_ci if (err == -EEXIST) 10062306a36Sopenharmony_ci return err; 10162306a36Sopenharmony_ci if (err) 10262306a36Sopenharmony_ci goto rollback; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci prestera_mall_prio_update(block, f); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci block->mall.bound = true; 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cirollback: 11162306a36Sopenharmony_ci list_for_each_entry_continue_reverse(binding, 11262306a36Sopenharmony_ci &block->binding_list, list) 11362306a36Sopenharmony_ci prestera_span_rule_del(binding, block->ingress); 11462306a36Sopenharmony_ci return err; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_civoid prestera_mall_destroy(struct prestera_flow_block *block) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct prestera_flow_block_binding *binding; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci list_for_each_entry(binding, &block->binding_list, list) 12262306a36Sopenharmony_ci prestera_span_rule_del(binding, block->ingress); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci block->mall.prio_min = UINT_MAX; 12562306a36Sopenharmony_ci block->mall.prio_max = 0; 12662306a36Sopenharmony_ci block->mall.bound = false; 12762306a36Sopenharmony_ci} 128