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_span.h" 11 12 struct prestera_span_entry { 13 struct list_head list; 14 struct prestera_port *port; 15 refcount_t ref_count; 16 u8 id; 17 }; 18 19 struct prestera_span { 20 struct prestera_switch *sw; 21 struct list_head entries; 22 }; 23 24 static struct prestera_span_entry * 25 prestera_span_entry_create(struct prestera_port *port, u8 span_id) 26 { 27 struct prestera_span_entry *entry; 28 29 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 30 if (!entry) 31 return ERR_PTR(-ENOMEM); 32 33 refcount_set(&entry->ref_count, 1); 34 entry->port = port; 35 entry->id = span_id; 36 list_add_tail(&entry->list, &port->sw->span->entries); 37 38 return entry; 39 } 40 41 static void prestera_span_entry_del(struct prestera_span_entry *entry) 42 { 43 list_del(&entry->list); 44 kfree(entry); 45 } 46 47 static struct prestera_span_entry * 48 prestera_span_entry_find_by_id(struct prestera_span *span, u8 span_id) 49 { 50 struct prestera_span_entry *entry; 51 52 list_for_each_entry(entry, &span->entries, list) { 53 if (entry->id == span_id) 54 return entry; 55 } 56 57 return NULL; 58 } 59 60 static struct prestera_span_entry * 61 prestera_span_entry_find_by_port(struct prestera_span *span, 62 struct prestera_port *port) 63 { 64 struct prestera_span_entry *entry; 65 66 list_for_each_entry(entry, &span->entries, list) { 67 if (entry->port == port) 68 return entry; 69 } 70 71 return NULL; 72 } 73 74 static int prestera_span_get(struct prestera_port *port, u8 *span_id) 75 { 76 u8 new_span_id; 77 struct prestera_switch *sw = port->sw; 78 struct prestera_span_entry *entry; 79 int err; 80 81 entry = prestera_span_entry_find_by_port(sw->span, port); 82 if (entry) { 83 refcount_inc(&entry->ref_count); 84 *span_id = entry->id; 85 return 0; 86 } 87 88 err = prestera_hw_span_get(port, &new_span_id); 89 if (err) 90 return err; 91 92 entry = prestera_span_entry_create(port, new_span_id); 93 if (IS_ERR(entry)) { 94 prestera_hw_span_release(sw, new_span_id); 95 return PTR_ERR(entry); 96 } 97 98 *span_id = new_span_id; 99 return 0; 100 } 101 102 static int prestera_span_put(struct prestera_switch *sw, u8 span_id) 103 { 104 struct prestera_span_entry *entry; 105 int err; 106 107 entry = prestera_span_entry_find_by_id(sw->span, span_id); 108 if (!entry) 109 return false; 110 111 if (!refcount_dec_and_test(&entry->ref_count)) 112 return 0; 113 114 err = prestera_hw_span_release(sw, span_id); 115 if (err) 116 return err; 117 118 prestera_span_entry_del(entry); 119 return 0; 120 } 121 122 static int prestera_span_rule_add(struct prestera_flow_block_binding *binding, 123 struct prestera_port *to_port) 124 { 125 struct prestera_switch *sw = binding->port->sw; 126 u8 span_id; 127 int err; 128 129 if (binding->span_id != PRESTERA_SPAN_INVALID_ID) 130 /* port already in mirroring */ 131 return -EEXIST; 132 133 err = prestera_span_get(to_port, &span_id); 134 if (err) 135 return err; 136 137 err = prestera_hw_span_bind(binding->port, span_id); 138 if (err) { 139 prestera_span_put(sw, span_id); 140 return err; 141 } 142 143 binding->span_id = span_id; 144 return 0; 145 } 146 147 static int prestera_span_rule_del(struct prestera_flow_block_binding *binding) 148 { 149 int err; 150 151 err = prestera_hw_span_unbind(binding->port); 152 if (err) 153 return err; 154 155 err = prestera_span_put(binding->port->sw, binding->span_id); 156 if (err) 157 return err; 158 159 binding->span_id = PRESTERA_SPAN_INVALID_ID; 160 return 0; 161 } 162 163 int prestera_span_replace(struct prestera_flow_block *block, 164 struct tc_cls_matchall_offload *f) 165 { 166 struct prestera_flow_block_binding *binding; 167 __be16 protocol = f->common.protocol; 168 struct flow_action_entry *act; 169 struct prestera_port *port; 170 int err; 171 172 if (!flow_offload_has_one_action(&f->rule->action)) { 173 NL_SET_ERR_MSG(f->common.extack, 174 "Only singular actions are supported"); 175 return -EOPNOTSUPP; 176 } 177 178 act = &f->rule->action.entries[0]; 179 180 if (!prestera_netdev_check(act->dev)) { 181 NL_SET_ERR_MSG(f->common.extack, 182 "Only Marvell Prestera port is supported"); 183 return -EINVAL; 184 } 185 if (!tc_cls_can_offload_and_chain0(act->dev, &f->common)) 186 return -EOPNOTSUPP; 187 if (act->id != FLOW_ACTION_MIRRED) 188 return -EOPNOTSUPP; 189 if (protocol != htons(ETH_P_ALL)) 190 return -EOPNOTSUPP; 191 192 port = netdev_priv(act->dev); 193 194 list_for_each_entry(binding, &block->binding_list, list) { 195 err = prestera_span_rule_add(binding, port); 196 if (err) 197 goto rollback; 198 } 199 200 return 0; 201 202 rollback: 203 list_for_each_entry_continue_reverse(binding, 204 &block->binding_list, list) 205 prestera_span_rule_del(binding); 206 return err; 207 } 208 209 void prestera_span_destroy(struct prestera_flow_block *block) 210 { 211 struct prestera_flow_block_binding *binding; 212 213 list_for_each_entry(binding, &block->binding_list, list) 214 prestera_span_rule_del(binding); 215 } 216 217 int prestera_span_init(struct prestera_switch *sw) 218 { 219 struct prestera_span *span; 220 221 span = kzalloc(sizeof(*span), GFP_KERNEL); 222 if (!span) 223 return -ENOMEM; 224 225 INIT_LIST_HEAD(&span->entries); 226 227 sw->span = span; 228 span->sw = sw; 229 230 return 0; 231 } 232 233 void prestera_span_fini(struct prestera_switch *sw) 234 { 235 struct prestera_span *span = sw->span; 236 237 WARN_ON(!list_empty(&span->entries)); 238 kfree(span); 239 } 240