162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2020 Marvell International Ltd. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/list.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "prestera.h" 862306a36Sopenharmony_ci#include "prestera_acl.h" 962306a36Sopenharmony_ci#include "prestera_flow.h" 1062306a36Sopenharmony_ci#include "prestera_flower.h" 1162306a36Sopenharmony_ci#include "prestera_matchall.h" 1262306a36Sopenharmony_ci#include "prestera_span.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic LIST_HEAD(prestera_block_cb_list); 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int prestera_flow_block_mall_cb(struct prestera_flow_block *block, 1762306a36Sopenharmony_ci struct tc_cls_matchall_offload *f) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci switch (f->command) { 2062306a36Sopenharmony_ci case TC_CLSMATCHALL_REPLACE: 2162306a36Sopenharmony_ci return prestera_mall_replace(block, f); 2262306a36Sopenharmony_ci case TC_CLSMATCHALL_DESTROY: 2362306a36Sopenharmony_ci prestera_mall_destroy(block); 2462306a36Sopenharmony_ci return 0; 2562306a36Sopenharmony_ci default: 2662306a36Sopenharmony_ci return -EOPNOTSUPP; 2762306a36Sopenharmony_ci } 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int prestera_flow_block_flower_cb(struct prestera_flow_block *block, 3162306a36Sopenharmony_ci struct flow_cls_offload *f) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci switch (f->command) { 3462306a36Sopenharmony_ci case FLOW_CLS_REPLACE: 3562306a36Sopenharmony_ci return prestera_flower_replace(block, f); 3662306a36Sopenharmony_ci case FLOW_CLS_DESTROY: 3762306a36Sopenharmony_ci prestera_flower_destroy(block, f); 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci case FLOW_CLS_STATS: 4062306a36Sopenharmony_ci return prestera_flower_stats(block, f); 4162306a36Sopenharmony_ci case FLOW_CLS_TMPLT_CREATE: 4262306a36Sopenharmony_ci return prestera_flower_tmplt_create(block, f); 4362306a36Sopenharmony_ci case FLOW_CLS_TMPLT_DESTROY: 4462306a36Sopenharmony_ci prestera_flower_tmplt_destroy(block, f); 4562306a36Sopenharmony_ci return 0; 4662306a36Sopenharmony_ci default: 4762306a36Sopenharmony_ci return -EOPNOTSUPP; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int prestera_flow_block_cb(enum tc_setup_type type, 5262306a36Sopenharmony_ci void *type_data, void *cb_priv) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct prestera_flow_block *block = cb_priv; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci switch (type) { 5762306a36Sopenharmony_ci case TC_SETUP_CLSFLOWER: 5862306a36Sopenharmony_ci return prestera_flow_block_flower_cb(block, type_data); 5962306a36Sopenharmony_ci case TC_SETUP_CLSMATCHALL: 6062306a36Sopenharmony_ci return prestera_flow_block_mall_cb(block, type_data); 6162306a36Sopenharmony_ci default: 6262306a36Sopenharmony_ci return -EOPNOTSUPP; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void prestera_flow_block_destroy(void *cb_priv) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct prestera_flow_block *block = cb_priv; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci prestera_flower_template_cleanup(block); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci WARN_ON(!list_empty(&block->template_list)); 7362306a36Sopenharmony_ci WARN_ON(!list_empty(&block->binding_list)); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci kfree(block); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic struct prestera_flow_block * 7962306a36Sopenharmony_ciprestera_flow_block_create(struct prestera_switch *sw, 8062306a36Sopenharmony_ci struct net *net, 8162306a36Sopenharmony_ci bool ingress) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct prestera_flow_block *block; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci block = kzalloc(sizeof(*block), GFP_KERNEL); 8662306a36Sopenharmony_ci if (!block) 8762306a36Sopenharmony_ci return NULL; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci INIT_LIST_HEAD(&block->binding_list); 9062306a36Sopenharmony_ci INIT_LIST_HEAD(&block->template_list); 9162306a36Sopenharmony_ci block->net = net; 9262306a36Sopenharmony_ci block->sw = sw; 9362306a36Sopenharmony_ci block->mall.prio_min = UINT_MAX; 9462306a36Sopenharmony_ci block->mall.prio_max = 0; 9562306a36Sopenharmony_ci block->mall.bound = false; 9662306a36Sopenharmony_ci block->ingress = ingress; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return block; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void prestera_flow_block_release(void *cb_priv) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct prestera_flow_block *block = cb_priv; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci prestera_flow_block_destroy(block); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic bool 10962306a36Sopenharmony_ciprestera_flow_block_is_bound(const struct prestera_flow_block *block) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci return block->ruleset_zero; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic struct prestera_flow_block_binding * 11562306a36Sopenharmony_ciprestera_flow_block_lookup(struct prestera_flow_block *block, 11662306a36Sopenharmony_ci struct prestera_port *port) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct prestera_flow_block_binding *binding; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci list_for_each_entry(binding, &block->binding_list, list) 12162306a36Sopenharmony_ci if (binding->port == port) 12262306a36Sopenharmony_ci return binding; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return NULL; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int prestera_flow_block_bind(struct prestera_flow_block *block, 12862306a36Sopenharmony_ci struct prestera_port *port) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct prestera_flow_block_binding *binding; 13162306a36Sopenharmony_ci int err; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci binding = kzalloc(sizeof(*binding), GFP_KERNEL); 13462306a36Sopenharmony_ci if (!binding) 13562306a36Sopenharmony_ci return -ENOMEM; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci binding->span_id = PRESTERA_SPAN_INVALID_ID; 13862306a36Sopenharmony_ci binding->port = port; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (prestera_flow_block_is_bound(block)) { 14162306a36Sopenharmony_ci err = prestera_acl_ruleset_bind(block->ruleset_zero, port); 14262306a36Sopenharmony_ci if (err) 14362306a36Sopenharmony_ci goto err_ruleset_bind; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci list_add(&binding->list, &block->binding_list); 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cierr_ruleset_bind: 15062306a36Sopenharmony_ci kfree(binding); 15162306a36Sopenharmony_ci return err; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int prestera_flow_block_unbind(struct prestera_flow_block *block, 15562306a36Sopenharmony_ci struct prestera_port *port) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct prestera_flow_block_binding *binding; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci binding = prestera_flow_block_lookup(block, port); 16062306a36Sopenharmony_ci if (!binding) 16162306a36Sopenharmony_ci return -ENOENT; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci list_del(&binding->list); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (prestera_flow_block_is_bound(block)) 16662306a36Sopenharmony_ci prestera_acl_ruleset_unbind(block->ruleset_zero, port); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci kfree(binding); 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic struct prestera_flow_block * 17362306a36Sopenharmony_ciprestera_flow_block_get(struct prestera_switch *sw, 17462306a36Sopenharmony_ci struct flow_block_offload *f, 17562306a36Sopenharmony_ci bool *register_block, 17662306a36Sopenharmony_ci bool ingress) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct prestera_flow_block *block; 17962306a36Sopenharmony_ci struct flow_block_cb *block_cb; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, 18262306a36Sopenharmony_ci prestera_flow_block_cb, sw); 18362306a36Sopenharmony_ci if (!block_cb) { 18462306a36Sopenharmony_ci block = prestera_flow_block_create(sw, f->net, ingress); 18562306a36Sopenharmony_ci if (!block) 18662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci block_cb = flow_block_cb_alloc(prestera_flow_block_cb, 18962306a36Sopenharmony_ci sw, block, 19062306a36Sopenharmony_ci prestera_flow_block_release); 19162306a36Sopenharmony_ci if (IS_ERR(block_cb)) { 19262306a36Sopenharmony_ci prestera_flow_block_destroy(block); 19362306a36Sopenharmony_ci return ERR_CAST(block_cb); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci block->block_cb = block_cb; 19762306a36Sopenharmony_ci *register_block = true; 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci block = flow_block_cb_priv(block_cb); 20062306a36Sopenharmony_ci *register_block = false; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci flow_block_cb_incref(block_cb); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return block; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void prestera_flow_block_put(struct prestera_flow_block *block) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct flow_block_cb *block_cb = block->block_cb; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (flow_block_cb_decref(block_cb)) 21362306a36Sopenharmony_ci return; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci flow_block_cb_free(block_cb); 21662306a36Sopenharmony_ci prestera_flow_block_destroy(block); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int prestera_setup_flow_block_bind(struct prestera_port *port, 22062306a36Sopenharmony_ci struct flow_block_offload *f, bool ingress) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 22362306a36Sopenharmony_ci struct prestera_flow_block *block; 22462306a36Sopenharmony_ci struct flow_block_cb *block_cb; 22562306a36Sopenharmony_ci bool register_block; 22662306a36Sopenharmony_ci int err; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci block = prestera_flow_block_get(sw, f, ®ister_block, ingress); 22962306a36Sopenharmony_ci if (IS_ERR(block)) 23062306a36Sopenharmony_ci return PTR_ERR(block); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci block_cb = block->block_cb; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci err = prestera_flow_block_bind(block, port); 23562306a36Sopenharmony_ci if (err) 23662306a36Sopenharmony_ci goto err_block_bind; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (register_block) { 23962306a36Sopenharmony_ci flow_block_cb_add(block_cb, f); 24062306a36Sopenharmony_ci list_add_tail(&block_cb->driver_list, &prestera_block_cb_list); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (ingress) 24462306a36Sopenharmony_ci port->ingress_flow_block = block; 24562306a36Sopenharmony_ci else 24662306a36Sopenharmony_ci port->egress_flow_block = block; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cierr_block_bind: 25162306a36Sopenharmony_ci prestera_flow_block_put(block); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return err; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic void prestera_setup_flow_block_unbind(struct prestera_port *port, 25762306a36Sopenharmony_ci struct flow_block_offload *f, bool ingress) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct prestera_switch *sw = port->sw; 26062306a36Sopenharmony_ci struct prestera_flow_block *block; 26162306a36Sopenharmony_ci struct flow_block_cb *block_cb; 26262306a36Sopenharmony_ci int err; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw); 26562306a36Sopenharmony_ci if (!block_cb) 26662306a36Sopenharmony_ci return; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci block = flow_block_cb_priv(block_cb); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci prestera_mall_destroy(block); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci err = prestera_flow_block_unbind(block, port); 27362306a36Sopenharmony_ci if (err) 27462306a36Sopenharmony_ci goto error; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (!flow_block_cb_decref(block_cb)) { 27762306a36Sopenharmony_ci flow_block_cb_remove(block_cb, f); 27862306a36Sopenharmony_ci list_del(&block_cb->driver_list); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_cierror: 28162306a36Sopenharmony_ci if (ingress) 28262306a36Sopenharmony_ci port->ingress_flow_block = NULL; 28362306a36Sopenharmony_ci else 28462306a36Sopenharmony_ci port->egress_flow_block = NULL; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int prestera_setup_flow_block_clsact(struct prestera_port *port, 28862306a36Sopenharmony_ci struct flow_block_offload *f, 28962306a36Sopenharmony_ci bool ingress) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci f->driver_block_list = &prestera_block_cb_list; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci switch (f->command) { 29462306a36Sopenharmony_ci case FLOW_BLOCK_BIND: 29562306a36Sopenharmony_ci return prestera_setup_flow_block_bind(port, f, ingress); 29662306a36Sopenharmony_ci case FLOW_BLOCK_UNBIND: 29762306a36Sopenharmony_ci prestera_setup_flow_block_unbind(port, f, ingress); 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci default: 30062306a36Sopenharmony_ci return -EOPNOTSUPP; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ciint prestera_flow_block_setup(struct prestera_port *port, 30562306a36Sopenharmony_ci struct flow_block_offload *f) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci switch (f->binder_type) { 30862306a36Sopenharmony_ci case FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS: 30962306a36Sopenharmony_ci return prestera_setup_flow_block_clsact(port, f, true); 31062306a36Sopenharmony_ci case FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS: 31162306a36Sopenharmony_ci return prestera_setup_flow_block_clsact(port, f, false); 31262306a36Sopenharmony_ci default: 31362306a36Sopenharmony_ci return -EOPNOTSUPP; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci} 316