prestera_span.c (5107B)
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 false; 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 123static int prestera_span_rule_add(struct prestera_flow_block_binding *binding, 124 struct prestera_port *to_port) 125{ 126 struct prestera_switch *sw = binding->port->sw; 127 u8 span_id; 128 int err; 129 130 if (binding->span_id != PRESTERA_SPAN_INVALID_ID) 131 /* port already in mirroring */ 132 return -EEXIST; 133 134 err = prestera_span_get(to_port, &span_id); 135 if (err) 136 return err; 137 138 err = prestera_hw_span_bind(binding->port, span_id); 139 if (err) { 140 prestera_span_put(sw, span_id); 141 return err; 142 } 143 144 binding->span_id = span_id; 145 return 0; 146} 147 148static int prestera_span_rule_del(struct prestera_flow_block_binding *binding) 149{ 150 int err; 151 152 err = prestera_hw_span_unbind(binding->port); 153 if (err) 154 return err; 155 156 err = prestera_span_put(binding->port->sw, binding->span_id); 157 if (err) 158 return err; 159 160 binding->span_id = PRESTERA_SPAN_INVALID_ID; 161 return 0; 162} 163 164int prestera_span_replace(struct prestera_flow_block *block, 165 struct tc_cls_matchall_offload *f) 166{ 167 struct prestera_flow_block_binding *binding; 168 __be16 protocol = f->common.protocol; 169 struct flow_action_entry *act; 170 struct prestera_port *port; 171 int err; 172 173 if (!flow_offload_has_one_action(&f->rule->action)) { 174 NL_SET_ERR_MSG(f->common.extack, 175 "Only singular actions are supported"); 176 return -EOPNOTSUPP; 177 } 178 179 act = &f->rule->action.entries[0]; 180 181 if (!prestera_netdev_check(act->dev)) { 182 NL_SET_ERR_MSG(f->common.extack, 183 "Only Marvell Prestera port is supported"); 184 return -EINVAL; 185 } 186 if (!tc_cls_can_offload_and_chain0(act->dev, &f->common)) 187 return -EOPNOTSUPP; 188 if (act->id != FLOW_ACTION_MIRRED) 189 return -EOPNOTSUPP; 190 if (protocol != htons(ETH_P_ALL)) 191 return -EOPNOTSUPP; 192 193 port = netdev_priv(act->dev); 194 195 list_for_each_entry(binding, &block->binding_list, list) { 196 err = prestera_span_rule_add(binding, port); 197 if (err) 198 goto rollback; 199 } 200 201 return 0; 202 203rollback: 204 list_for_each_entry_continue_reverse(binding, 205 &block->binding_list, list) 206 prestera_span_rule_del(binding); 207 return err; 208} 209 210void prestera_span_destroy(struct prestera_flow_block *block) 211{ 212 struct prestera_flow_block_binding *binding; 213 214 list_for_each_entry(binding, &block->binding_list, list) 215 prestera_span_rule_del(binding); 216} 217 218int prestera_span_init(struct prestera_switch *sw) 219{ 220 struct prestera_span *span; 221 222 span = kzalloc(sizeof(*span), GFP_KERNEL); 223 if (!span) 224 return -ENOMEM; 225 226 INIT_LIST_HEAD(&span->entries); 227 228 sw->span = span; 229 span->sw = sw; 230 231 return 0; 232} 233 234void prestera_span_fini(struct prestera_switch *sw) 235{ 236 struct prestera_span *span = sw->span; 237 238 WARN_ON(!list_empty(&span->entries)); 239 kfree(span); 240}