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
13struct prestera_span_entry {
14 struct list_head list;
15 struct prestera_port *port;
16 refcount_t ref_count;
17 u8 id;
18};
19
20struct prestera_span {
21 struct prestera_switch *sw;
22 struct list_head entries;
23};
24
25static struct prestera_span_entry *
26prestera_span_entry_create(struct prestera_port *port, u8 span_id)
27{
28 struct prestera_span_entry *entry;
29
30 entry = kzalloc(size: sizeof(*entry), GFP_KERNEL);
31 if (!entry)
32 return ERR_PTR(error: -ENOMEM);
33
34 refcount_set(r: &entry->ref_count, n: 1);
35 entry->port = port;
36 entry->id = span_id;
37 list_add_tail(new: &entry->list, head: &port->sw->span->entries);
38
39 return entry;
40}
41
42static void prestera_span_entry_del(struct prestera_span_entry *entry)
43{
44 list_del(entry: &entry->list);
45 kfree(objp: entry);
46}
47
48static struct prestera_span_entry *
49prestera_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
61static struct prestera_span_entry *
62prestera_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
75static 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(span: sw->span, port);
83 if (entry) {
84 refcount_inc(r: &entry->ref_count);
85 *span_id = entry->id;
86 return 0;
87 }
88
89 err = prestera_hw_span_get(port, span_id: &new_span_id);
90 if (err)
91 return err;
92
93 entry = prestera_span_entry_create(port, span_id: new_span_id);
94 if (IS_ERR(ptr: entry)) {
95 prestera_hw_span_release(sw, span_id: new_span_id);
96 return PTR_ERR(ptr: entry);
97 }
98
99 *span_id = new_span_id;
100 return 0;
101}
102
103static 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(span: sw->span, span_id);
109 if (!entry)
110 return -ENOENT;
111
112 if (!refcount_dec_and_test(r: &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
123int prestera_span_rule_add(struct prestera_flow_block_binding *binding,
124 struct prestera_port *to_port,
125 bool ingress)
126{
127 struct prestera_switch *sw = binding->port->sw;
128 u8 span_id;
129 int err;
130
131 if (binding->span_id != PRESTERA_SPAN_INVALID_ID)
132 /* port already in mirroring */
133 return -EEXIST;
134
135 err = prestera_span_get(port: to_port, span_id: &span_id);
136 if (err)
137 return err;
138
139 err = prestera_hw_span_bind(port: binding->port, span_id, ingress);
140 if (err) {
141 prestera_span_put(sw, span_id);
142 return err;
143 }
144
145 binding->span_id = span_id;
146 return 0;
147}
148
149int prestera_span_rule_del(struct prestera_flow_block_binding *binding,
150 bool ingress)
151{
152 int err;
153
154 if (binding->span_id == PRESTERA_SPAN_INVALID_ID)
155 return -ENOENT;
156
157 err = prestera_hw_span_unbind(port: binding->port, ingress);
158 if (err)
159 return err;
160
161 err = prestera_span_put(sw: binding->port->sw, span_id: binding->span_id);
162 if (err)
163 return err;
164
165 binding->span_id = PRESTERA_SPAN_INVALID_ID;
166 return 0;
167}
168
169int prestera_span_init(struct prestera_switch *sw)
170{
171 struct prestera_span *span;
172
173 span = kzalloc(size: sizeof(*span), GFP_KERNEL);
174 if (!span)
175 return -ENOMEM;
176
177 INIT_LIST_HEAD(list: &span->entries);
178
179 sw->span = span;
180 span->sw = sw;
181
182 return 0;
183}
184
185void prestera_span_fini(struct prestera_switch *sw)
186{
187 struct prestera_span *span = sw->span;
188
189 WARN_ON(!list_empty(&span->entries));
190 kfree(objp: span);
191}
192

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