192 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* 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