]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
mlxsw: spectrum_span: Extract mlxsw_sp_span_entry_{de, }configure()
[mirror_ubuntu-jammy-kernel.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_span.c
CommitLineData
a629ef21
PM
1/*
2 * drivers/net/ethernet/mellanox/mlxsw/mlxsw_span.c
3 * Copyright (c) 2018 Mellanox Technologies. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the names of the copyright holders nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 *
17 * Alternatively, this software may be distributed under the terms of the
18 * GNU General Public License ("GPL") version 2 as published by the Free
19 * Software Foundation.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <linux/list.h>
35
36#include "spectrum.h"
37#include "spectrum_span.h"
38
39int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
40{
41 int i;
42
43 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_SPAN))
44 return -EIO;
45
46 mlxsw_sp->span.entries_count = MLXSW_CORE_RES_GET(mlxsw_sp->core,
47 MAX_SPAN);
48 mlxsw_sp->span.entries = kcalloc(mlxsw_sp->span.entries_count,
49 sizeof(struct mlxsw_sp_span_entry),
50 GFP_KERNEL);
51 if (!mlxsw_sp->span.entries)
52 return -ENOMEM;
53
3546b03f
PM
54 for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
55 struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
56
57 INIT_LIST_HEAD(&curr->bound_ports_list);
58 curr->id = i;
59 }
a629ef21
PM
60
61 return 0;
62}
63
64void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp)
65{
66 int i;
67
68 for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
69 struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
70
71 WARN_ON_ONCE(!list_empty(&curr->bound_ports_list));
72 }
73 kfree(mlxsw_sp->span.entries);
74}
75
7b2ef81f
PM
76static int
77mlxsw_sp_span_entry_configure(struct mlxsw_sp *mlxsw_sp,
78 struct mlxsw_sp_span_entry *span_entry,
79 u8 local_port)
80{
81 char mpat_pl[MLXSW_REG_MPAT_LEN];
82 int pa_id = span_entry->id;
83
84 /* Create a new port analayzer entry for local_port. */
85 mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
86 MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
87 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
88}
89
90static void
91mlxsw_sp_span_entry_deconfigure(struct mlxsw_sp *mlxsw_sp,
92 struct mlxsw_sp_span_entry *span_entry)
93{
94 u8 local_port = span_entry->local_port;
95 char mpat_pl[MLXSW_REG_MPAT_LEN];
96 int pa_id = span_entry->id;
97
98 mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false,
99 MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
100 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
101}
102
a629ef21
PM
103static struct mlxsw_sp_span_entry *
104mlxsw_sp_span_entry_create(struct mlxsw_sp_port *port)
105{
3546b03f 106 struct mlxsw_sp_span_entry *span_entry = NULL;
a629ef21 107 struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
a629ef21 108 u8 local_port = port->local_port;
a629ef21 109 int i;
a629ef21
PM
110
111 /* find a free entry to use */
a629ef21
PM
112 for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
113 if (!mlxsw_sp->span.entries[i].ref_count) {
a629ef21
PM
114 span_entry = &mlxsw_sp->span.entries[i];
115 break;
116 }
117 }
3546b03f 118 if (!span_entry)
a629ef21
PM
119 return NULL;
120
7b2ef81f 121 if (mlxsw_sp_span_entry_configure(mlxsw_sp, span_entry, local_port))
a629ef21
PM
122 return NULL;
123
a629ef21
PM
124 span_entry->ref_count = 1;
125 span_entry->local_port = local_port;
126 return span_entry;
127}
128
129static void mlxsw_sp_span_entry_destroy(struct mlxsw_sp *mlxsw_sp,
130 struct mlxsw_sp_span_entry *span_entry)
131{
7b2ef81f 132 mlxsw_sp_span_entry_deconfigure(mlxsw_sp, span_entry);
a629ef21
PM
133}
134
135struct mlxsw_sp_span_entry *
98977089 136mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp, u8 local_port)
a629ef21
PM
137{
138 int i;
139
140 for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
141 struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
142
143 if (curr->ref_count && curr->local_port == local_port)
144 return curr;
145 }
146 return NULL;
147}
148
98977089
PM
149static struct mlxsw_sp_span_entry *
150mlxsw_sp_span_entry_find_by_id(struct mlxsw_sp *mlxsw_sp, int span_id)
151{
152 int i;
153
154 for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
155 struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
156
157 if (curr->ref_count && curr->id == span_id)
158 return curr;
159 }
160 return NULL;
161}
162
a629ef21
PM
163static struct mlxsw_sp_span_entry *
164mlxsw_sp_span_entry_get(struct mlxsw_sp_port *port)
165{
166 struct mlxsw_sp_span_entry *span_entry;
167
98977089
PM
168 span_entry = mlxsw_sp_span_entry_find_by_port(port->mlxsw_sp,
169 port->local_port);
a629ef21
PM
170 if (span_entry) {
171 /* Already exists, just take a reference */
172 span_entry->ref_count++;
173 return span_entry;
174 }
175
176 return mlxsw_sp_span_entry_create(port);
177}
178
179static int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp,
180 struct mlxsw_sp_span_entry *span_entry)
181{
182 WARN_ON(!span_entry->ref_count);
183 if (--span_entry->ref_count == 0)
184 mlxsw_sp_span_entry_destroy(mlxsw_sp, span_entry);
185 return 0;
186}
187
188static bool mlxsw_sp_span_is_egress_mirror(struct mlxsw_sp_port *port)
189{
190 struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
191 struct mlxsw_sp_span_inspected_port *p;
192 int i;
193
194 for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
195 struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
196
197 list_for_each_entry(p, &curr->bound_ports_list, list)
198 if (p->local_port == port->local_port &&
199 p->type == MLXSW_SP_SPAN_EGRESS)
200 return true;
201 }
202
203 return false;
204}
205
206static int mlxsw_sp_span_mtu_to_buffsize(const struct mlxsw_sp *mlxsw_sp,
207 int mtu)
208{
209 return mlxsw_sp_bytes_cells(mlxsw_sp, mtu * 5 / 2) + 1;
210}
211
212int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu)
213{
214 struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
215 char sbib_pl[MLXSW_REG_SBIB_LEN];
216 int err;
217
218 /* If port is egress mirrored, the shared buffer size should be
219 * updated according to the mtu value
220 */
221 if (mlxsw_sp_span_is_egress_mirror(port)) {
222 u32 buffsize = mlxsw_sp_span_mtu_to_buffsize(mlxsw_sp, mtu);
223
224 mlxsw_reg_sbib_pack(sbib_pl, port->local_port, buffsize);
225 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
226 if (err) {
227 netdev_err(port->dev, "Could not update shared buffer for mirroring\n");
228 return err;
229 }
230 }
231
232 return 0;
233}
234
235static struct mlxsw_sp_span_inspected_port *
236mlxsw_sp_span_entry_bound_port_find(struct mlxsw_sp_port *port,
237 struct mlxsw_sp_span_entry *span_entry)
238{
239 struct mlxsw_sp_span_inspected_port *p;
240
241 list_for_each_entry(p, &span_entry->bound_ports_list, list)
242 if (port->local_port == p->local_port)
243 return p;
244 return NULL;
245}
246
247static int
248mlxsw_sp_span_inspected_port_bind(struct mlxsw_sp_port *port,
249 struct mlxsw_sp_span_entry *span_entry,
250 enum mlxsw_sp_span_type type,
251 bool bind)
252{
253 struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
254 char mpar_pl[MLXSW_REG_MPAR_LEN];
255 int pa_id = span_entry->id;
256
257 /* bind the port to the SPAN entry */
258 mlxsw_reg_mpar_pack(mpar_pl, port->local_port,
259 (enum mlxsw_reg_mpar_i_e)type, bind, pa_id);
260 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl);
261}
262
263static int
264mlxsw_sp_span_inspected_port_add(struct mlxsw_sp_port *port,
265 struct mlxsw_sp_span_entry *span_entry,
266 enum mlxsw_sp_span_type type,
267 bool bind)
268{
269 struct mlxsw_sp_span_inspected_port *inspected_port;
270 struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
271 char sbib_pl[MLXSW_REG_SBIB_LEN];
272 int err;
273
274 /* if it is an egress SPAN, bind a shared buffer to it */
275 if (type == MLXSW_SP_SPAN_EGRESS) {
276 u32 buffsize = mlxsw_sp_span_mtu_to_buffsize(mlxsw_sp,
277 port->dev->mtu);
278
279 mlxsw_reg_sbib_pack(sbib_pl, port->local_port, buffsize);
280 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
281 if (err) {
282 netdev_err(port->dev, "Could not create shared buffer for mirroring\n");
283 return err;
284 }
285 }
286
287 if (bind) {
288 err = mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
289 true);
290 if (err)
291 goto err_port_bind;
292 }
293
294 inspected_port = kzalloc(sizeof(*inspected_port), GFP_KERNEL);
295 if (!inspected_port) {
296 err = -ENOMEM;
297 goto err_inspected_port_alloc;
298 }
299 inspected_port->local_port = port->local_port;
300 inspected_port->type = type;
301 list_add_tail(&inspected_port->list, &span_entry->bound_ports_list);
302
303 return 0;
304
305err_inspected_port_alloc:
306 if (bind)
307 mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
308 false);
309err_port_bind:
310 if (type == MLXSW_SP_SPAN_EGRESS) {
311 mlxsw_reg_sbib_pack(sbib_pl, port->local_port, 0);
312 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
313 }
314 return err;
315}
316
317static void
318mlxsw_sp_span_inspected_port_del(struct mlxsw_sp_port *port,
319 struct mlxsw_sp_span_entry *span_entry,
320 enum mlxsw_sp_span_type type,
321 bool bind)
322{
323 struct mlxsw_sp_span_inspected_port *inspected_port;
324 struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
325 char sbib_pl[MLXSW_REG_SBIB_LEN];
326
327 inspected_port = mlxsw_sp_span_entry_bound_port_find(port, span_entry);
328 if (!inspected_port)
329 return;
330
331 if (bind)
332 mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
333 false);
334 /* remove the SBIB buffer if it was egress SPAN */
335 if (type == MLXSW_SP_SPAN_EGRESS) {
336 mlxsw_reg_sbib_pack(sbib_pl, port->local_port, 0);
337 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
338 }
339
340 mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
341
342 list_del(&inspected_port->list);
343 kfree(inspected_port);
344}
345
346int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
347 struct mlxsw_sp_port *to,
98977089
PM
348 enum mlxsw_sp_span_type type, bool bind,
349 int *p_span_id)
a629ef21
PM
350{
351 struct mlxsw_sp *mlxsw_sp = from->mlxsw_sp;
352 struct mlxsw_sp_span_entry *span_entry;
353 int err;
354
355 span_entry = mlxsw_sp_span_entry_get(to);
356 if (!span_entry)
357 return -ENOENT;
358
359 netdev_dbg(from->dev, "Adding inspected port to SPAN entry %d\n",
360 span_entry->id);
361
362 err = mlxsw_sp_span_inspected_port_add(from, span_entry, type, bind);
363 if (err)
364 goto err_port_bind;
365
98977089 366 *p_span_id = span_entry->id;
a629ef21
PM
367 return 0;
368
369err_port_bind:
370 mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
371 return err;
372}
373
98977089 374void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, int span_id,
a629ef21
PM
375 enum mlxsw_sp_span_type type, bool bind)
376{
377 struct mlxsw_sp_span_entry *span_entry;
378
98977089 379 span_entry = mlxsw_sp_span_entry_find_by_id(from->mlxsw_sp, span_id);
a629ef21
PM
380 if (!span_entry) {
381 netdev_err(from->dev, "no span entry found\n");
382 return;
383 }
384
385 netdev_dbg(from->dev, "removing inspected port from SPAN entry %d\n",
386 span_entry->id);
387 mlxsw_sp_span_inspected_port_del(from, span_entry, type, bind);
388}