File Index
Files and directories
Sign in
FILE

prestera_span.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/marvell/prestera/prestera_span.c C
  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}
~/ Read-only file preview.