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_acl.h" 9 #include "prestera_flow.h" 10 #include "prestera_flower.h" 11 #include "prestera_matchall.h" 12 #include "prestera_span.h" 13 14 static LIST_HEAD(prestera_block_cb_list); 15 16 static int prestera_flow_block_mall_cb(struct prestera_flow_block *block, 17 struct tc_cls_matchall_offload *f) 18 { 19 switch (f->command) { 20 case TC_CLSMATCHALL_REPLACE: 21 return prestera_mall_replace(block, f); 22 case TC_CLSMATCHALL_DESTROY: 23 prestera_mall_destroy(block); 24 return 0; 25 default: 26 return -EOPNOTSUPP; 27 } 28 } 29 30 static int prestera_flow_block_flower_cb(struct prestera_flow_block *block, 31 struct flow_cls_offload *f) 32 { 33 switch (f->command) { 34 case FLOW_CLS_REPLACE: 35 return prestera_flower_replace(block, f); 36 case FLOW_CLS_DESTROY: 37 prestera_flower_destroy(block, f); 38 return 0; 39 case FLOW_CLS_STATS: 40 return prestera_flower_stats(block, f); 41 case FLOW_CLS_TMPLT_CREATE: 42 return prestera_flower_tmplt_create(block, f); 43 case FLOW_CLS_TMPLT_DESTROY: 44 prestera_flower_tmplt_destroy(block, f); 45 return 0; 46 default: 47 return -EOPNOTSUPP; 48 } 49 } 50 51 static int prestera_flow_block_cb(enum tc_setup_type type, 52 void *type_data, void *cb_priv) 53 { 54 struct prestera_flow_block *block = cb_priv; 55 56 switch (type) { 57 case TC_SETUP_CLSFLOWER: 58 return prestera_flow_block_flower_cb(block, type_data); 59 case TC_SETUP_CLSMATCHALL: 60 return prestera_flow_block_mall_cb(block, type_data); 61 default: 62 return -EOPNOTSUPP; 63 } 64 } 65 66 static void prestera_flow_block_destroy(void *cb_priv) 67 { 68 struct prestera_flow_block *block = cb_priv; 69 70 prestera_flower_template_cleanup(block); 71 72 WARN_ON(!list_empty(&block->template_list)); 73 WARN_ON(!list_empty(&block->binding_list)); 74 75 kfree(block); 76 } 77 78 static struct prestera_flow_block * 79 prestera_flow_block_create(struct prestera_switch *sw, 80 struct net *net, 81 bool ingress) 82 { 83 struct prestera_flow_block *block; 84 85 block = kzalloc(sizeof(*block), GFP_KERNEL); 86 if (!block) 87 return NULL; 88 89 INIT_LIST_HEAD(&block->binding_list); 90 INIT_LIST_HEAD(&block->template_list); 91 block->net = net; 92 block->sw = sw; 93 block->mall.prio_min = UINT_MAX; 94 block->mall.prio_max = 0; 95 block->mall.bound = false; 96 block->ingress = ingress; 97 98 return block; 99 } 100 101 static void prestera_flow_block_release(void *cb_priv) 102 { 103 struct prestera_flow_block *block = cb_priv; 104 105 prestera_flow_block_destroy(block); 106 } 107 108 static bool 109 prestera_flow_block_is_bound(const struct prestera_flow_block *block) 110 { 111 return block->ruleset_zero; 112 } 113 114 static struct prestera_flow_block_binding * 115 prestera_flow_block_lookup(struct prestera_flow_block *block, 116 struct prestera_port *port) 117 { 118 struct prestera_flow_block_binding *binding; 119 120 list_for_each_entry(binding, &block->binding_list, list) 121 if (binding->port == port) 122 return binding; 123 124 return NULL; 125 } 126 127 static int prestera_flow_block_bind(struct prestera_flow_block *block, 128 struct prestera_port *port) 129 { 130 struct prestera_flow_block_binding *binding; 131 int err; 132 133 binding = kzalloc(sizeof(*binding), GFP_KERNEL); 134 if (!binding) 135 return -ENOMEM; 136 137 binding->span_id = PRESTERA_SPAN_INVALID_ID; 138 binding->port = port; 139 140 if (prestera_flow_block_is_bound(block)) { 141 err = prestera_acl_ruleset_bind(block->ruleset_zero, port); 142 if (err) 143 goto err_ruleset_bind; 144 } 145 146 list_add(&binding->list, &block->binding_list); 147 return 0; 148 149 err_ruleset_bind: 150 kfree(binding); 151 return err; 152 } 153 154 static int prestera_flow_block_unbind(struct prestera_flow_block *block, 155 struct prestera_port *port) 156 { 157 struct prestera_flow_block_binding *binding; 158 159 binding = prestera_flow_block_lookup(block, port); 160 if (!binding) 161 return -ENOENT; 162 163 list_del(&binding->list); 164 165 if (prestera_flow_block_is_bound(block)) 166 prestera_acl_ruleset_unbind(block->ruleset_zero, port); 167 168 kfree(binding); 169 return 0; 170 } 171 172 static struct prestera_flow_block * 173 prestera_flow_block_get(struct prestera_switch *sw, 174 struct flow_block_offload *f, 175 bool *register_block, 176 bool ingress) 177 { 178 struct prestera_flow_block *block; 179 struct flow_block_cb *block_cb; 180 181 block_cb = flow_block_cb_lookup(f->block, 182 prestera_flow_block_cb, sw); 183 if (!block_cb) { 184 block = prestera_flow_block_create(sw, f->net, ingress); 185 if (!block) 186 return ERR_PTR(-ENOMEM); 187 188 block_cb = flow_block_cb_alloc(prestera_flow_block_cb, 189 sw, block, 190 prestera_flow_block_release); 191 if (IS_ERR(block_cb)) { 192 prestera_flow_block_destroy(block); 193 return ERR_CAST(block_cb); 194 } 195 196 block->block_cb = block_cb; 197 *register_block = true; 198 } else { 199 block = flow_block_cb_priv(block_cb); 200 *register_block = false; 201 } 202 203 flow_block_cb_incref(block_cb); 204 205 return block; 206 } 207 208 static void prestera_flow_block_put(struct prestera_flow_block *block) 209 { 210 struct flow_block_cb *block_cb = block->block_cb; 211 212 if (flow_block_cb_decref(block_cb)) 213 return; 214 215 flow_block_cb_free(block_cb); 216 prestera_flow_block_destroy(block); 217 } 218 219 static int prestera_setup_flow_block_bind(struct prestera_port *port, 220 struct flow_block_offload *f, bool ingress) 221 { 222 struct prestera_switch *sw = port->sw; 223 struct prestera_flow_block *block; 224 struct flow_block_cb *block_cb; 225 bool register_block; 226 int err; 227 228 block = prestera_flow_block_get(sw, f, ®ister_block, ingress); 229 if (IS_ERR(block)) 230 return PTR_ERR(block); 231 232 block_cb = block->block_cb; 233 234 err = prestera_flow_block_bind(block, port); 235 if (err) 236 goto err_block_bind; 237 238 if (register_block) { 239 flow_block_cb_add(block_cb, f); 240 list_add_tail(&block_cb->driver_list, &prestera_block_cb_list); 241 } 242 243 if (ingress) 244 port->ingress_flow_block = block; 245 else 246 port->egress_flow_block = block; 247 248 return 0; 249 250 err_block_bind: 251 prestera_flow_block_put(block); 252 253 return err; 254 } 255 256 static void prestera_setup_flow_block_unbind(struct prestera_port *port, 257 struct flow_block_offload *f, bool ingress) 258 { 259 struct prestera_switch *sw = port->sw; 260 struct prestera_flow_block *block; 261 struct flow_block_cb *block_cb; 262 int err; 263 264 block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw); 265 if (!block_cb) 266 return; 267 268 block = flow_block_cb_priv(block_cb); 269 270 prestera_mall_destroy(block); 271 272 err = prestera_flow_block_unbind(block, port); 273 if (err) 274 goto error; 275 276 if (!flow_block_cb_decref(block_cb)) { 277 flow_block_cb_remove(block_cb, f); 278 list_del(&block_cb->driver_list); 279 } 280 error: 281 if (ingress) 282 port->ingress_flow_block = NULL; 283 else 284 port->egress_flow_block = NULL; 285 } 286 287 static int prestera_setup_flow_block_clsact(struct prestera_port *port, 288 struct flow_block_offload *f, 289 bool ingress) 290 { 291 f->driver_block_list = &prestera_block_cb_list; 292 293 switch (f->command) { 294 case FLOW_BLOCK_BIND: 295 return prestera_setup_flow_block_bind(port, f, ingress); 296 case FLOW_BLOCK_UNBIND: 297 prestera_setup_flow_block_unbind(port, f, ingress); 298 return 0; 299 default: 300 return -EOPNOTSUPP; 301 } 302 } 303 304 int prestera_flow_block_setup(struct prestera_port *port, 305 struct flow_block_offload *f) 306 { 307 switch (f->binder_type) { 308 case FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS: 309 return prestera_setup_flow_block_clsact(port, f, true); 310 case FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS: 311 return prestera_setup_flow_block_clsact(port, f, false); 312 default: 313 return -EOPNOTSUPP; 314 } 315 } 316