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