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
14static 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
44int 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
55static 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
62int 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
110rollback:
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
117void 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