Merge tag 'nvme-6.1-2022-11-10' of git://git.infradead.org/nvme into block-6.1
[linux-block.git] / drivers / net / ethernet / marvell / prestera / prestera_matchall.c
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, 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
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(&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)
100                         goto rollback;
101         }
102
103         prestera_mall_prio_update(block, f);
104
105         block->mall.bound = true;
106         return 0;
107
108 rollback:
109         list_for_each_entry_continue_reverse(binding,
110                                              &block->binding_list, list)
111                 prestera_span_rule_del(binding, block->ingress);
112         return err;
113 }
114
115 void prestera_mall_destroy(struct prestera_flow_block *block)
116 {
117         struct prestera_flow_block_binding *binding;
118
119         list_for_each_entry(binding, &block->binding_list, list)
120                 prestera_span_rule_del(binding, block->ingress);
121
122         block->mall.prio_min = UINT_MAX;
123         block->mall.prio_max = 0;
124         block->mall.bound = false;
125 }