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, 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 | |
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(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 | |
110 | rollback: |
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 | |
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, ingress: block->ingress); |
123 | |
124 | block->mall.prio_min = UINT_MAX; |
125 | block->mall.prio_max = 0; |
126 | block->mall.bound = false; |
127 | } |
128 | |