1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2/* Copyright (c) 2020 Marvell International Ltd. All rights reserved */ 3 4#include <linux/kernel.h> 5#include <linux/list.h> 6 7#include "prestera.h" 8#include "prestera_hw.h" 9#include "prestera_acl.h" 10#include "prestera_flow.h" 11#include "prestera_span.h" 12 13struct prestera_span_entry { 14 struct list_head list; 15 struct prestera_port *port; 16 refcount_t ref_count; 17 u8 id; 18}; 19 20struct prestera_span { 21 struct prestera_switch *sw; 22 struct list_head entries; 23}; 24 25static struct prestera_span_entry * 26prestera_span_entry_create(struct prestera_port *port, u8 span_id) 27{ 28 struct prestera_span_entry *entry; 29 30 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 31 if (!entry) 32 return ERR_PTR(-ENOMEM); 33 34 refcount_set(&entry->ref_count, 1); 35 entry->port = port; 36 entry->id = span_id; 37 list_add_tail(&entry->list, &port->sw->span->entries); 38 39 return entry; 40} 41 42static void prestera_span_entry_del(struct prestera_span_entry *entry) 43{ 44 list_del(&entry->list); 45 kfree(entry); 46} 47 48static struct prestera_span_entry * 49prestera_span_entry_find_by_id(struct prestera_span *span, u8 span_id) 50{ 51 struct prestera_span_entry *entry; 52 53 list_for_each_entry(entry, &span->entries, list) { 54 if (entry->id == span_id) 55 return entry; 56 } 57 58 return NULL; 59} 60 61static struct prestera_span_entry * 62prestera_span_entry_find_by_port(struct prestera_span *span, 63 struct prestera_port *port) 64{ 65 struct prestera_span_entry *entry; 66 67 list_for_each_entry(entry, &span->entries, list) { 68 if (entry->port == port) 69 return entry; 70 } 71 72 return NULL; 73} 74 75static int prestera_span_get(struct prestera_port *port, u8 *span_id) 76{ 77 u8 new_span_id; 78 struct prestera_switch *sw = port->sw; 79 struct prestera_span_entry *entry; 80 int err; 81 82 entry = prestera_span_entry_find_by_port(sw->span, port); 83 if (entry) { 84 refcount_inc(&entry->ref_count); 85 *span_id = entry->id; 86 return 0; 87 } 88 89 err = prestera_hw_span_get(port, &new_span_id); 90 if (err) 91 return err; 92 93 entry = prestera_span_entry_create(port, new_span_id); 94 if (IS_ERR(entry)) { 95 prestera_hw_span_release(sw, new_span_id); 96 return PTR_ERR(entry); 97 } 98 99 *span_id = new_span_id; 100 return 0; 101} 102 103static int prestera_span_put(struct prestera_switch *sw, u8 span_id) 104{ 105 struct prestera_span_entry *entry; 106 int err; 107 108 entry = prestera_span_entry_find_by_id(sw->span, span_id); 109 if (!entry) 110 return -ENOENT; 111 112 if (!refcount_dec_and_test(&entry->ref_count)) 113 return 0; 114 115 err = prestera_hw_span_release(sw, span_id); 116 if (err) 117 return err; 118 119 prestera_span_entry_del(entry); 120 return 0; 121} 122 123int prestera_span_rule_add(struct prestera_flow_block_binding *binding, 124 struct prestera_port *to_port, 125 bool ingress) 126{ 127 struct prestera_switch *sw = binding->port->sw; 128 u8 span_id; 129 int err; 130 131 if (binding->span_id != PRESTERA_SPAN_INVALID_ID) 132 /* port already in mirroring */ 133 return -EEXIST; 134 135 err = prestera_span_get(to_port, &span_id); 136 if (err) 137 return err; 138 139 err = prestera_hw_span_bind(binding->port, span_id, ingress); 140 if (err) { 141 prestera_span_put(sw, span_id); 142 return err; 143 } 144 145 binding->span_id = span_id; 146 return 0; 147} 148 149int prestera_span_rule_del(struct prestera_flow_block_binding *binding, 150 bool ingress) 151{ 152 int err; 153 154 if (binding->span_id == PRESTERA_SPAN_INVALID_ID) 155 return -ENOENT; 156 157 err = prestera_hw_span_unbind(binding->port, ingress); 158 if (err) 159 return err; 160 161 err = prestera_span_put(binding->port->sw, binding->span_id); 162 if (err) 163 return err; 164 165 binding->span_id = PRESTERA_SPAN_INVALID_ID; 166 return 0; 167} 168 169int prestera_span_init(struct prestera_switch *sw) 170{ 171 struct prestera_span *span; 172 173 span = kzalloc(sizeof(*span), GFP_KERNEL); 174 if (!span) 175 return -ENOMEM; 176 177 INIT_LIST_HEAD(&span->entries); 178 179 sw->span = span; 180 span->sw = sw; 181 182 return 0; 183} 184 185void prestera_span_fini(struct prestera_switch *sw) 186{ 187 struct prestera_span *span = sw->span; 188 189 WARN_ON(!list_empty(&span->entries)); 190 kfree(span); 191} 192