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