1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
4 #include <linux/kernel.h>
5 #include <linux/list.h>
8 #include "prestera_hw.h"
9 #include "prestera_acl.h"
10 #include "prestera_span.h"
12 struct prestera_span_entry {
13 struct list_head list;
14 struct prestera_port *port;
19 struct prestera_span {
20 struct prestera_switch *sw;
21 struct list_head entries;
24 static struct prestera_span_entry *
25 prestera_span_entry_create(struct prestera_port *port, u8 span_id)
27 struct prestera_span_entry *entry;
29 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
31 return ERR_PTR(-ENOMEM);
33 refcount_set(&entry->ref_count, 1);
36 list_add_tail(&entry->list, &port->sw->span->entries);
41 static void prestera_span_entry_del(struct prestera_span_entry *entry)
43 list_del(&entry->list);
47 static struct prestera_span_entry *
48 prestera_span_entry_find_by_id(struct prestera_span *span, u8 span_id)
50 struct prestera_span_entry *entry;
52 list_for_each_entry(entry, &span->entries, list) {
53 if (entry->id == span_id)
60 static struct prestera_span_entry *
61 prestera_span_entry_find_by_port(struct prestera_span *span,
62 struct prestera_port *port)
64 struct prestera_span_entry *entry;
66 list_for_each_entry(entry, &span->entries, list) {
67 if (entry->port == port)
74 static int prestera_span_get(struct prestera_port *port, u8 *span_id)
77 struct prestera_switch *sw = port->sw;
78 struct prestera_span_entry *entry;
81 entry = prestera_span_entry_find_by_port(sw->span, port);
83 refcount_inc(&entry->ref_count);
88 err = prestera_hw_span_get(port, &new_span_id);
92 entry = prestera_span_entry_create(port, new_span_id);
94 prestera_hw_span_release(sw, new_span_id);
95 return PTR_ERR(entry);
98 *span_id = new_span_id;
102 static int prestera_span_put(struct prestera_switch *sw, u8 span_id)
104 struct prestera_span_entry *entry;
107 entry = prestera_span_entry_find_by_id(sw->span, span_id);
111 if (!refcount_dec_and_test(&entry->ref_count))
114 err = prestera_hw_span_release(sw, span_id);
118 prestera_span_entry_del(entry);
122 static int prestera_span_rule_add(struct prestera_flow_block_binding *binding,
123 struct prestera_port *to_port)
125 struct prestera_switch *sw = binding->port->sw;
129 if (binding->span_id != PRESTERA_SPAN_INVALID_ID)
130 /* port already in mirroring */
133 err = prestera_span_get(to_port, &span_id);
137 err = prestera_hw_span_bind(binding->port, span_id);
139 prestera_span_put(sw, span_id);
143 binding->span_id = span_id;
147 static int prestera_span_rule_del(struct prestera_flow_block_binding *binding)
151 err = prestera_hw_span_unbind(binding->port);
155 err = prestera_span_put(binding->port->sw, binding->span_id);
159 binding->span_id = PRESTERA_SPAN_INVALID_ID;
163 int prestera_span_replace(struct prestera_flow_block *block,
164 struct tc_cls_matchall_offload *f)
166 struct prestera_flow_block_binding *binding;
167 __be16 protocol = f->common.protocol;
168 struct flow_action_entry *act;
169 struct prestera_port *port;
172 if (!flow_offload_has_one_action(&f->rule->action)) {
173 NL_SET_ERR_MSG(f->common.extack,
174 "Only singular actions are supported");
178 act = &f->rule->action.entries[0];
180 if (!prestera_netdev_check(act->dev)) {
181 NL_SET_ERR_MSG(f->common.extack,
182 "Only Marvell Prestera port is supported");
185 if (!tc_cls_can_offload_and_chain0(act->dev, &f->common))
187 if (act->id != FLOW_ACTION_MIRRED)
189 if (protocol != htons(ETH_P_ALL))
192 port = netdev_priv(act->dev);
194 list_for_each_entry(binding, &block->binding_list, list) {
195 err = prestera_span_rule_add(binding, port);
203 list_for_each_entry_continue_reverse(binding,
204 &block->binding_list, list)
205 prestera_span_rule_del(binding);
209 void prestera_span_destroy(struct prestera_flow_block *block)
211 struct prestera_flow_block_binding *binding;
213 list_for_each_entry(binding, &block->binding_list, list)
214 prestera_span_rule_del(binding);
217 int prestera_span_init(struct prestera_switch *sw)
219 struct prestera_span *span;
221 span = kzalloc(sizeof(*span), GFP_KERNEL);
225 INIT_LIST_HEAD(&span->entries);
233 void prestera_span_fini(struct prestera_switch *sw)
235 struct prestera_span *span = sw->span;
237 WARN_ON(!list_empty(&span->entries));