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