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, chain_index: f->common.chain_index,
22 prio_min: &flower_prio_min, prio_max: &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(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(dev: 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(dev: act->dev, common: &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(dev: act->dev);
96
97 list_for_each_entry(binding, &block->binding_list, list) {
98 err = prestera_span_rule_add(binding, to_port: port, ingress: 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, ingress: 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, ingress: block->ingress);
123
124 block->mall.prio_min = UINT_MAX;
125 block->mall.prio_max = 0;
126 block->mall.bound = false;
127}
128

source code of linux/drivers/net/ethernet/marvell/prestera/prestera_matchall.c