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 13 struct prestera_span_entry { 14 struct list_head list; 15 struct prestera_port *port; 16 refcount_t ref_count; 17 u8 id; 18 }; 19 20 struct prestera_span { 21 struct prestera_switch *sw; 22 struct list_head entries; 23 }; 24 25 static struct prestera_span_entry * 26 prestera_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 42 static void prestera_span_entry_del(struct prestera_span_entry *entry) 43 { 44 list_del(&entry->list); 45 kfree(entry); 46 } 47 48 static struct prestera_span_entry * 49 prestera_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 61 static struct prestera_span_entry * 62 prestera_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 75 static 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 103 static 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 123 int 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 149 int 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 169 int 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 185 void 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