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