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