]> Git Repo - J-linux.git/blob - drivers/net/ethernet/marvell/prestera/prestera_span.c
Merge remote-tracking branch 'spi/for-5.14' into spi-linus
[J-linux.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_span.h"
11
12 struct prestera_span_entry {
13         struct list_head list;
14         struct prestera_port *port;
15         refcount_t ref_count;
16         u8 id;
17 };
18
19 struct prestera_span {
20         struct prestera_switch *sw;
21         struct list_head entries;
22 };
23
24 static struct prestera_span_entry *
25 prestera_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
41 static void prestera_span_entry_del(struct prestera_span_entry *entry)
42 {
43         list_del(&entry->list);
44         kfree(entry);
45 }
46
47 static struct prestera_span_entry *
48 prestera_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
60 static struct prestera_span_entry *
61 prestera_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
74 static 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
102 static 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
122 static 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                 /* port already in mirroring */
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
147 static 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
163 int 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
202 rollback:
203         list_for_each_entry_continue_reverse(binding,
204                                              &block->binding_list, list)
205                 prestera_span_rule_del(binding);
206         return err;
207 }
208
209 void 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
217 int 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
233 void 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 }
This page took 0.048783 seconds and 4 git commands to generate.