GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / net / ethernet / marvell / prestera / prestera_span.c
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2020 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_acl.h"
10 #include "prestera_flow.h"
11 #include "prestera_span.h"
12
13 struct prestera_span_entry {
14         struct list_head list;
15         struct prestera_port *port;
16         refcount_t ref_count;
17         u8 id;
18 };
19
20 struct prestera_span {
21         struct prestera_switch *sw;
22         struct list_head entries;
23 };
24
25 static struct prestera_span_entry *
26 prestera_span_entry_create(struct prestera_port *port, u8 span_id)
27 {
28         struct prestera_span_entry *entry;
29
30         entry = kzalloc(sizeof(*entry), GFP_KERNEL);
31         if (!entry)
32                 return ERR_PTR(-ENOMEM);
33
34         refcount_set(&entry->ref_count, 1);
35         entry->port = port;
36         entry->id = span_id;
37         list_add_tail(&entry->list, &port->sw->span->entries);
38
39         return entry;
40 }
41
42 static void prestera_span_entry_del(struct prestera_span_entry *entry)
43 {
44         list_del(&entry->list);
45         kfree(entry);
46 }
47
48 static struct prestera_span_entry *
49 prestera_span_entry_find_by_id(struct prestera_span *span, u8 span_id)
50 {
51         struct prestera_span_entry *entry;
52
53         list_for_each_entry(entry, &span->entries, list) {
54                 if (entry->id == span_id)
55                         return entry;
56         }
57
58         return NULL;
59 }
60
61 static struct prestera_span_entry *
62 prestera_span_entry_find_by_port(struct prestera_span *span,
63                                  struct prestera_port *port)
64 {
65         struct prestera_span_entry *entry;
66
67         list_for_each_entry(entry, &span->entries, list) {
68                 if (entry->port == port)
69                         return entry;
70         }
71
72         return NULL;
73 }
74
75 static int prestera_span_get(struct prestera_port *port, u8 *span_id)
76 {
77         u8 new_span_id;
78         struct prestera_switch *sw = port->sw;
79         struct prestera_span_entry *entry;
80         int err;
81
82         entry = prestera_span_entry_find_by_port(sw->span, port);
83         if (entry) {
84                 refcount_inc(&entry->ref_count);
85                 *span_id = entry->id;
86                 return 0;
87         }
88
89         err = prestera_hw_span_get(port, &new_span_id);
90         if (err)
91                 return err;
92
93         entry = prestera_span_entry_create(port, new_span_id);
94         if (IS_ERR(entry)) {
95                 prestera_hw_span_release(sw, new_span_id);
96                 return PTR_ERR(entry);
97         }
98
99         *span_id = new_span_id;
100         return 0;
101 }
102
103 static int prestera_span_put(struct prestera_switch *sw, u8 span_id)
104 {
105         struct prestera_span_entry *entry;
106         int err;
107
108         entry = prestera_span_entry_find_by_id(sw->span, span_id);
109         if (!entry)
110                 return false;
111
112         if (!refcount_dec_and_test(&entry->ref_count))
113                 return 0;
114
115         err = prestera_hw_span_release(sw, span_id);
116         if (err)
117                 return err;
118
119         prestera_span_entry_del(entry);
120         return 0;
121 }
122
123 static int prestera_span_rule_add(struct prestera_flow_block_binding *binding,
124                                   struct prestera_port *to_port)
125 {
126         struct prestera_switch *sw = binding->port->sw;
127         u8 span_id;
128         int err;
129
130         if (binding->span_id != PRESTERA_SPAN_INVALID_ID)
131                 /* port already in mirroring */
132                 return -EEXIST;
133
134         err = prestera_span_get(to_port, &span_id);
135         if (err)
136                 return err;
137
138         err = prestera_hw_span_bind(binding->port, span_id);
139         if (err) {
140                 prestera_span_put(sw, span_id);
141                 return err;
142         }
143
144         binding->span_id = span_id;
145         return 0;
146 }
147
148 static int prestera_span_rule_del(struct prestera_flow_block_binding *binding)
149 {
150         int err;
151
152         err = prestera_hw_span_unbind(binding->port);
153         if (err)
154                 return err;
155
156         err = prestera_span_put(binding->port->sw, binding->span_id);
157         if (err)
158                 return err;
159
160         binding->span_id = PRESTERA_SPAN_INVALID_ID;
161         return 0;
162 }
163
164 int prestera_span_replace(struct prestera_flow_block *block,
165                           struct tc_cls_matchall_offload *f)
166 {
167         struct prestera_flow_block_binding *binding;
168         __be16 protocol = f->common.protocol;
169         struct flow_action_entry *act;
170         struct prestera_port *port;
171         int err;
172
173         if (!flow_offload_has_one_action(&f->rule->action)) {
174                 NL_SET_ERR_MSG(f->common.extack,
175                                "Only singular actions are supported");
176                 return -EOPNOTSUPP;
177         }
178
179         act = &f->rule->action.entries[0];
180
181         if (!prestera_netdev_check(act->dev)) {
182                 NL_SET_ERR_MSG(f->common.extack,
183                                "Only Marvell Prestera port is supported");
184                 return -EINVAL;
185         }
186         if (!tc_cls_can_offload_and_chain0(act->dev, &f->common))
187                 return -EOPNOTSUPP;
188         if (act->id != FLOW_ACTION_MIRRED)
189                 return -EOPNOTSUPP;
190         if (protocol != htons(ETH_P_ALL))
191                 return -EOPNOTSUPP;
192
193         port = netdev_priv(act->dev);
194
195         list_for_each_entry(binding, &block->binding_list, list) {
196                 err = prestera_span_rule_add(binding, port);
197                 if (err)
198                         goto rollback;
199         }
200
201         return 0;
202
203 rollback:
204         list_for_each_entry_continue_reverse(binding,
205                                              &block->binding_list, list)
206                 prestera_span_rule_del(binding);
207         return err;
208 }
209
210 void prestera_span_destroy(struct prestera_flow_block *block)
211 {
212         struct prestera_flow_block_binding *binding;
213
214         list_for_each_entry(binding, &block->binding_list, list)
215                 prestera_span_rule_del(binding);
216 }
217
218 int prestera_span_init(struct prestera_switch *sw)
219 {
220         struct prestera_span *span;
221
222         span = kzalloc(sizeof(*span), GFP_KERNEL);
223         if (!span)
224                 return -ENOMEM;
225
226         INIT_LIST_HEAD(&span->entries);
227
228         sw->span = span;
229         span->sw = sw;
230
231         return 0;
232 }
233
234 void prestera_span_fini(struct prestera_switch *sw)
235 {
236         struct prestera_span *span = sw->span;
237
238         WARN_ON(!list_empty(&span->entries));
239         kfree(span);
240 }