162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/list.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "prestera.h"
862306a36Sopenharmony_ci#include "prestera_hw.h"
962306a36Sopenharmony_ci#include "prestera_acl.h"
1062306a36Sopenharmony_ci#include "prestera_flow.h"
1162306a36Sopenharmony_ci#include "prestera_span.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistruct prestera_span_entry {
1462306a36Sopenharmony_ci	struct list_head list;
1562306a36Sopenharmony_ci	struct prestera_port *port;
1662306a36Sopenharmony_ci	refcount_t ref_count;
1762306a36Sopenharmony_ci	u8 id;
1862306a36Sopenharmony_ci};
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct prestera_span {
2162306a36Sopenharmony_ci	struct prestera_switch *sw;
2262306a36Sopenharmony_ci	struct list_head entries;
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic struct prestera_span_entry *
2662306a36Sopenharmony_ciprestera_span_entry_create(struct prestera_port *port, u8 span_id)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct prestera_span_entry *entry;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
3162306a36Sopenharmony_ci	if (!entry)
3262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	refcount_set(&entry->ref_count, 1);
3562306a36Sopenharmony_ci	entry->port = port;
3662306a36Sopenharmony_ci	entry->id = span_id;
3762306a36Sopenharmony_ci	list_add_tail(&entry->list, &port->sw->span->entries);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return entry;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void prestera_span_entry_del(struct prestera_span_entry *entry)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	list_del(&entry->list);
4562306a36Sopenharmony_ci	kfree(entry);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic struct prestera_span_entry *
4962306a36Sopenharmony_ciprestera_span_entry_find_by_id(struct prestera_span *span, u8 span_id)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct prestera_span_entry *entry;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	list_for_each_entry(entry, &span->entries, list) {
5462306a36Sopenharmony_ci		if (entry->id == span_id)
5562306a36Sopenharmony_ci			return entry;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return NULL;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic struct prestera_span_entry *
6262306a36Sopenharmony_ciprestera_span_entry_find_by_port(struct prestera_span *span,
6362306a36Sopenharmony_ci				 struct prestera_port *port)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct prestera_span_entry *entry;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	list_for_each_entry(entry, &span->entries, list) {
6862306a36Sopenharmony_ci		if (entry->port == port)
6962306a36Sopenharmony_ci			return entry;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return NULL;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int prestera_span_get(struct prestera_port *port, u8 *span_id)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	u8 new_span_id;
7862306a36Sopenharmony_ci	struct prestera_switch *sw = port->sw;
7962306a36Sopenharmony_ci	struct prestera_span_entry *entry;
8062306a36Sopenharmony_ci	int err;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	entry = prestera_span_entry_find_by_port(sw->span, port);
8362306a36Sopenharmony_ci	if (entry) {
8462306a36Sopenharmony_ci		refcount_inc(&entry->ref_count);
8562306a36Sopenharmony_ci		*span_id = entry->id;
8662306a36Sopenharmony_ci		return 0;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	err = prestera_hw_span_get(port, &new_span_id);
9062306a36Sopenharmony_ci	if (err)
9162306a36Sopenharmony_ci		return err;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	entry = prestera_span_entry_create(port, new_span_id);
9462306a36Sopenharmony_ci	if (IS_ERR(entry)) {
9562306a36Sopenharmony_ci		prestera_hw_span_release(sw, new_span_id);
9662306a36Sopenharmony_ci		return PTR_ERR(entry);
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	*span_id = new_span_id;
10062306a36Sopenharmony_ci	return 0;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int prestera_span_put(struct prestera_switch *sw, u8 span_id)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct prestera_span_entry *entry;
10662306a36Sopenharmony_ci	int err;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	entry = prestera_span_entry_find_by_id(sw->span, span_id);
10962306a36Sopenharmony_ci	if (!entry)
11062306a36Sopenharmony_ci		return -ENOENT;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (!refcount_dec_and_test(&entry->ref_count))
11362306a36Sopenharmony_ci		return 0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	err = prestera_hw_span_release(sw, span_id);
11662306a36Sopenharmony_ci	if (err)
11762306a36Sopenharmony_ci		return err;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	prestera_span_entry_del(entry);
12062306a36Sopenharmony_ci	return 0;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ciint prestera_span_rule_add(struct prestera_flow_block_binding *binding,
12462306a36Sopenharmony_ci			   struct prestera_port *to_port,
12562306a36Sopenharmony_ci			   bool ingress)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct prestera_switch *sw = binding->port->sw;
12862306a36Sopenharmony_ci	u8 span_id;
12962306a36Sopenharmony_ci	int err;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (binding->span_id != PRESTERA_SPAN_INVALID_ID)
13262306a36Sopenharmony_ci		/* port already in mirroring */
13362306a36Sopenharmony_ci		return -EEXIST;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	err = prestera_span_get(to_port, &span_id);
13662306a36Sopenharmony_ci	if (err)
13762306a36Sopenharmony_ci		return err;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	err = prestera_hw_span_bind(binding->port, span_id, ingress);
14062306a36Sopenharmony_ci	if (err) {
14162306a36Sopenharmony_ci		prestera_span_put(sw, span_id);
14262306a36Sopenharmony_ci		return err;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	binding->span_id = span_id;
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciint prestera_span_rule_del(struct prestera_flow_block_binding *binding,
15062306a36Sopenharmony_ci			   bool ingress)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	int err;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (binding->span_id == PRESTERA_SPAN_INVALID_ID)
15562306a36Sopenharmony_ci		return -ENOENT;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	err = prestera_hw_span_unbind(binding->port, ingress);
15862306a36Sopenharmony_ci	if (err)
15962306a36Sopenharmony_ci		return err;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	err = prestera_span_put(binding->port->sw, binding->span_id);
16262306a36Sopenharmony_ci	if (err)
16362306a36Sopenharmony_ci		return err;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	binding->span_id = PRESTERA_SPAN_INVALID_ID;
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ciint prestera_span_init(struct prestera_switch *sw)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct prestera_span *span;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	span = kzalloc(sizeof(*span), GFP_KERNEL);
17462306a36Sopenharmony_ci	if (!span)
17562306a36Sopenharmony_ci		return -ENOMEM;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	INIT_LIST_HEAD(&span->entries);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	sw->span = span;
18062306a36Sopenharmony_ci	span->sw = sw;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_civoid prestera_span_fini(struct prestera_switch *sw)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct prestera_span *span = sw->span;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	WARN_ON(!list_empty(&span->entries));
19062306a36Sopenharmony_ci	kfree(span);
19162306a36Sopenharmony_ci}
192