113defa27SSerhiy Boiko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
213defa27SSerhiy Boiko /* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
313defa27SSerhiy Boiko
413defa27SSerhiy Boiko #include <linux/kernel.h>
513defa27SSerhiy Boiko #include <linux/list.h>
613defa27SSerhiy Boiko
713defa27SSerhiy Boiko #include "prestera.h"
813defa27SSerhiy Boiko #include "prestera_hw.h"
913defa27SSerhiy Boiko #include "prestera_acl.h"
1047327e19SVolodymyr Mytnyk #include "prestera_flow.h"
1113defa27SSerhiy Boiko #include "prestera_span.h"
1213defa27SSerhiy Boiko
1313defa27SSerhiy Boiko struct prestera_span_entry {
1413defa27SSerhiy Boiko struct list_head list;
1513defa27SSerhiy Boiko struct prestera_port *port;
1613defa27SSerhiy Boiko refcount_t ref_count;
1713defa27SSerhiy Boiko u8 id;
1813defa27SSerhiy Boiko };
1913defa27SSerhiy Boiko
2013defa27SSerhiy Boiko struct prestera_span {
2113defa27SSerhiy Boiko struct prestera_switch *sw;
2213defa27SSerhiy Boiko struct list_head entries;
2313defa27SSerhiy Boiko };
2413defa27SSerhiy Boiko
2513defa27SSerhiy Boiko static struct prestera_span_entry *
prestera_span_entry_create(struct prestera_port * port,u8 span_id)2613defa27SSerhiy Boiko prestera_span_entry_create(struct prestera_port *port, u8 span_id)
2713defa27SSerhiy Boiko {
2813defa27SSerhiy Boiko struct prestera_span_entry *entry;
2913defa27SSerhiy Boiko
3013defa27SSerhiy Boiko entry = kzalloc(sizeof(*entry), GFP_KERNEL);
3113defa27SSerhiy Boiko if (!entry)
3213defa27SSerhiy Boiko return ERR_PTR(-ENOMEM);
3313defa27SSerhiy Boiko
3413defa27SSerhiy Boiko refcount_set(&entry->ref_count, 1);
3513defa27SSerhiy Boiko entry->port = port;
3613defa27SSerhiy Boiko entry->id = span_id;
3713defa27SSerhiy Boiko list_add_tail(&entry->list, &port->sw->span->entries);
3813defa27SSerhiy Boiko
3913defa27SSerhiy Boiko return entry;
4013defa27SSerhiy Boiko }
4113defa27SSerhiy Boiko
prestera_span_entry_del(struct prestera_span_entry * entry)4213defa27SSerhiy Boiko static void prestera_span_entry_del(struct prestera_span_entry *entry)
4313defa27SSerhiy Boiko {
4413defa27SSerhiy Boiko list_del(&entry->list);
4513defa27SSerhiy Boiko kfree(entry);
4613defa27SSerhiy Boiko }
4713defa27SSerhiy Boiko
4813defa27SSerhiy Boiko static struct prestera_span_entry *
prestera_span_entry_find_by_id(struct prestera_span * span,u8 span_id)4913defa27SSerhiy Boiko prestera_span_entry_find_by_id(struct prestera_span *span, u8 span_id)
5013defa27SSerhiy Boiko {
5113defa27SSerhiy Boiko struct prestera_span_entry *entry;
5213defa27SSerhiy Boiko
5313defa27SSerhiy Boiko list_for_each_entry(entry, &span->entries, list) {
5413defa27SSerhiy Boiko if (entry->id == span_id)
5513defa27SSerhiy Boiko return entry;
5613defa27SSerhiy Boiko }
5713defa27SSerhiy Boiko
5813defa27SSerhiy Boiko return NULL;
5913defa27SSerhiy Boiko }
6013defa27SSerhiy Boiko
6113defa27SSerhiy Boiko static struct prestera_span_entry *
prestera_span_entry_find_by_port(struct prestera_span * span,struct prestera_port * port)6213defa27SSerhiy Boiko prestera_span_entry_find_by_port(struct prestera_span *span,
6313defa27SSerhiy Boiko struct prestera_port *port)
6413defa27SSerhiy Boiko {
6513defa27SSerhiy Boiko struct prestera_span_entry *entry;
6613defa27SSerhiy Boiko
6713defa27SSerhiy Boiko list_for_each_entry(entry, &span->entries, list) {
6813defa27SSerhiy Boiko if (entry->port == port)
6913defa27SSerhiy Boiko return entry;
7013defa27SSerhiy Boiko }
7113defa27SSerhiy Boiko
7213defa27SSerhiy Boiko return NULL;
7313defa27SSerhiy Boiko }
7413defa27SSerhiy Boiko
prestera_span_get(struct prestera_port * port,u8 * span_id)7513defa27SSerhiy Boiko static int prestera_span_get(struct prestera_port *port, u8 *span_id)
7613defa27SSerhiy Boiko {
7713defa27SSerhiy Boiko u8 new_span_id;
7813defa27SSerhiy Boiko struct prestera_switch *sw = port->sw;
7913defa27SSerhiy Boiko struct prestera_span_entry *entry;
8013defa27SSerhiy Boiko int err;
8113defa27SSerhiy Boiko
8213defa27SSerhiy Boiko entry = prestera_span_entry_find_by_port(sw->span, port);
8313defa27SSerhiy Boiko if (entry) {
8413defa27SSerhiy Boiko refcount_inc(&entry->ref_count);
8513defa27SSerhiy Boiko *span_id = entry->id;
8613defa27SSerhiy Boiko return 0;
8713defa27SSerhiy Boiko }
8813defa27SSerhiy Boiko
8913defa27SSerhiy Boiko err = prestera_hw_span_get(port, &new_span_id);
9013defa27SSerhiy Boiko if (err)
9113defa27SSerhiy Boiko return err;
9213defa27SSerhiy Boiko
9313defa27SSerhiy Boiko entry = prestera_span_entry_create(port, new_span_id);
9413defa27SSerhiy Boiko if (IS_ERR(entry)) {
9513defa27SSerhiy Boiko prestera_hw_span_release(sw, new_span_id);
9613defa27SSerhiy Boiko return PTR_ERR(entry);
9713defa27SSerhiy Boiko }
9813defa27SSerhiy Boiko
9913defa27SSerhiy Boiko *span_id = new_span_id;
10013defa27SSerhiy Boiko return 0;
10113defa27SSerhiy Boiko }
10213defa27SSerhiy Boiko
prestera_span_put(struct prestera_switch * sw,u8 span_id)10313defa27SSerhiy Boiko static int prestera_span_put(struct prestera_switch *sw, u8 span_id)
10413defa27SSerhiy Boiko {
10513defa27SSerhiy Boiko struct prestera_span_entry *entry;
10613defa27SSerhiy Boiko int err;
10713defa27SSerhiy Boiko
10813defa27SSerhiy Boiko entry = prestera_span_entry_find_by_id(sw->span, span_id);
10913defa27SSerhiy Boiko if (!entry)
110*32391e64SMaksym Glubokiy return -ENOENT;
11113defa27SSerhiy Boiko
11213defa27SSerhiy Boiko if (!refcount_dec_and_test(&entry->ref_count))
11313defa27SSerhiy Boiko return 0;
11413defa27SSerhiy Boiko
11513defa27SSerhiy Boiko err = prestera_hw_span_release(sw, span_id);
11613defa27SSerhiy Boiko if (err)
11713defa27SSerhiy Boiko return err;
11813defa27SSerhiy Boiko
11913defa27SSerhiy Boiko prestera_span_entry_del(entry);
12013defa27SSerhiy Boiko return 0;
12113defa27SSerhiy Boiko }
12213defa27SSerhiy Boiko
prestera_span_rule_add(struct prestera_flow_block_binding * binding,struct prestera_port * to_port,bool ingress)1238afd552dSSerhiy Boiko int prestera_span_rule_add(struct prestera_flow_block_binding *binding,
1248c448c2bSSerhiy Boiko struct prestera_port *to_port,
1258c448c2bSSerhiy Boiko bool ingress)
12613defa27SSerhiy Boiko {
12713defa27SSerhiy Boiko struct prestera_switch *sw = binding->port->sw;
12813defa27SSerhiy Boiko u8 span_id;
12913defa27SSerhiy Boiko int err;
13013defa27SSerhiy Boiko
13113defa27SSerhiy Boiko if (binding->span_id != PRESTERA_SPAN_INVALID_ID)
13213defa27SSerhiy Boiko /* port already in mirroring */
13313defa27SSerhiy Boiko return -EEXIST;
13413defa27SSerhiy Boiko
13513defa27SSerhiy Boiko err = prestera_span_get(to_port, &span_id);
13613defa27SSerhiy Boiko if (err)
13713defa27SSerhiy Boiko return err;
13813defa27SSerhiy Boiko
1398c448c2bSSerhiy Boiko err = prestera_hw_span_bind(binding->port, span_id, ingress);
14013defa27SSerhiy Boiko if (err) {
14113defa27SSerhiy Boiko prestera_span_put(sw, span_id);
14213defa27SSerhiy Boiko return err;
14313defa27SSerhiy Boiko }
14413defa27SSerhiy Boiko
14513defa27SSerhiy Boiko binding->span_id = span_id;
14613defa27SSerhiy Boiko return 0;
14713defa27SSerhiy Boiko }
14813defa27SSerhiy Boiko
prestera_span_rule_del(struct prestera_flow_block_binding * binding,bool ingress)1498c448c2bSSerhiy Boiko int prestera_span_rule_del(struct prestera_flow_block_binding *binding,
1508c448c2bSSerhiy Boiko bool ingress)
15113defa27SSerhiy Boiko {
15213defa27SSerhiy Boiko int err;
15313defa27SSerhiy Boiko
154*32391e64SMaksym Glubokiy if (binding->span_id == PRESTERA_SPAN_INVALID_ID)
155*32391e64SMaksym Glubokiy return -ENOENT;
156*32391e64SMaksym Glubokiy
1578c448c2bSSerhiy Boiko err = prestera_hw_span_unbind(binding->port, ingress);
15813defa27SSerhiy Boiko if (err)
15913defa27SSerhiy Boiko return err;
16013defa27SSerhiy Boiko
16113defa27SSerhiy Boiko err = prestera_span_put(binding->port->sw, binding->span_id);
16213defa27SSerhiy Boiko if (err)
16313defa27SSerhiy Boiko return err;
16413defa27SSerhiy Boiko
16513defa27SSerhiy Boiko binding->span_id = PRESTERA_SPAN_INVALID_ID;
16613defa27SSerhiy Boiko return 0;
16713defa27SSerhiy Boiko }
16813defa27SSerhiy Boiko
prestera_span_init(struct prestera_switch * sw)16913defa27SSerhiy Boiko int prestera_span_init(struct prestera_switch *sw)
17013defa27SSerhiy Boiko {
17113defa27SSerhiy Boiko struct prestera_span *span;
17213defa27SSerhiy Boiko
17313defa27SSerhiy Boiko span = kzalloc(sizeof(*span), GFP_KERNEL);
17413defa27SSerhiy Boiko if (!span)
17513defa27SSerhiy Boiko return -ENOMEM;
17613defa27SSerhiy Boiko
17713defa27SSerhiy Boiko INIT_LIST_HEAD(&span->entries);
17813defa27SSerhiy Boiko
17913defa27SSerhiy Boiko sw->span = span;
18013defa27SSerhiy Boiko span->sw = sw;
18113defa27SSerhiy Boiko
18213defa27SSerhiy Boiko return 0;
18313defa27SSerhiy Boiko }
18413defa27SSerhiy Boiko
prestera_span_fini(struct prestera_switch * sw)18513defa27SSerhiy Boiko void prestera_span_fini(struct prestera_switch *sw)
18613defa27SSerhiy Boiko {
18713defa27SSerhiy Boiko struct prestera_span *span = sw->span;
18813defa27SSerhiy Boiko
18913defa27SSerhiy Boiko WARN_ON(!list_empty(&span->entries));
19013defa27SSerhiy Boiko kfree(span);
19113defa27SSerhiy Boiko }
192