316 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* 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 14static LIST_HEAD(prestera_block_cb_list);15 16static 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 30static 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 51static 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 66static 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 78static struct prestera_flow_block *79prestera_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 101static 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 108static bool109prestera_flow_block_is_bound(const struct prestera_flow_block *block)110{111 return block->ruleset_zero;112}113 114static struct prestera_flow_block_binding *115prestera_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 127static 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 149err_ruleset_bind:150 kfree(binding);151 return err;152}153 154static 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 172static struct prestera_flow_block *173prestera_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 208static 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 219static 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 else246 port->egress_flow_block = block;247 248 return 0;249 250err_block_bind:251 prestera_flow_block_put(block);252 253 return err;254}255 256static 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 }280error:281 if (ingress)282 port->ingress_flow_block = NULL;283 else284 port->egress_flow_block = NULL;285}286 287static 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 304int 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