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