162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2017-2020 Mellanox Technologies. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/slab.h> 662306a36Sopenharmony_ci#include <linux/errno.h> 762306a36Sopenharmony_ci#include <linux/list.h> 862306a36Sopenharmony_ci#include <net/net_namespace.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "spectrum.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistruct mlxsw_sp_flow_block * 1362306a36Sopenharmony_cimlxsw_sp_flow_block_create(struct mlxsw_sp *mlxsw_sp, struct net *net) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci struct mlxsw_sp_flow_block *block; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci block = kzalloc(sizeof(*block), GFP_KERNEL); 1862306a36Sopenharmony_ci if (!block) 1962306a36Sopenharmony_ci return NULL; 2062306a36Sopenharmony_ci INIT_LIST_HEAD(&block->binding_list); 2162306a36Sopenharmony_ci INIT_LIST_HEAD(&block->mall.list); 2262306a36Sopenharmony_ci block->mlxsw_sp = mlxsw_sp; 2362306a36Sopenharmony_ci block->net = net; 2462306a36Sopenharmony_ci return block; 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_civoid mlxsw_sp_flow_block_destroy(struct mlxsw_sp_flow_block *block) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci WARN_ON(!list_empty(&block->binding_list)); 3062306a36Sopenharmony_ci kfree(block); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct mlxsw_sp_flow_block_binding * 3462306a36Sopenharmony_cimlxsw_sp_flow_block_lookup(struct mlxsw_sp_flow_block *block, 3562306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, bool ingress) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct mlxsw_sp_flow_block_binding *binding; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci list_for_each_entry(binding, &block->binding_list, list) 4062306a36Sopenharmony_ci if (binding->mlxsw_sp_port == mlxsw_sp_port && 4162306a36Sopenharmony_ci binding->ingress == ingress) 4262306a36Sopenharmony_ci return binding; 4362306a36Sopenharmony_ci return NULL; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic bool 4762306a36Sopenharmony_cimlxsw_sp_flow_block_ruleset_bound(const struct mlxsw_sp_flow_block *block) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci return block->ruleset_zero; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int mlxsw_sp_flow_block_bind(struct mlxsw_sp *mlxsw_sp, 5362306a36Sopenharmony_ci struct mlxsw_sp_flow_block *block, 5462306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 5562306a36Sopenharmony_ci bool ingress, 5662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct mlxsw_sp_flow_block_binding *binding; 5962306a36Sopenharmony_ci int err; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (WARN_ON(mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress))) 6262306a36Sopenharmony_ci return -EEXIST; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (ingress && block->ingress_blocker_rule_count) { 6562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to ingress because it contains unsupported rules"); 6662306a36Sopenharmony_ci return -EOPNOTSUPP; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (!ingress && block->egress_blocker_rule_count) { 7062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to egress because it contains unsupported rules"); 7162306a36Sopenharmony_ci return -EOPNOTSUPP; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci err = mlxsw_sp_mall_port_bind(block, mlxsw_sp_port, extack); 7562306a36Sopenharmony_ci if (err) 7662306a36Sopenharmony_ci return err; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci binding = kzalloc(sizeof(*binding), GFP_KERNEL); 7962306a36Sopenharmony_ci if (!binding) { 8062306a36Sopenharmony_ci err = -ENOMEM; 8162306a36Sopenharmony_ci goto err_binding_alloc; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci binding->mlxsw_sp_port = mlxsw_sp_port; 8462306a36Sopenharmony_ci binding->ingress = ingress; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (mlxsw_sp_flow_block_ruleset_bound(block)) { 8762306a36Sopenharmony_ci err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding); 8862306a36Sopenharmony_ci if (err) 8962306a36Sopenharmony_ci goto err_ruleset_bind; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (ingress) 9362306a36Sopenharmony_ci block->ingress_binding_count++; 9462306a36Sopenharmony_ci else 9562306a36Sopenharmony_ci block->egress_binding_count++; 9662306a36Sopenharmony_ci list_add(&binding->list, &block->binding_list); 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cierr_ruleset_bind: 10062306a36Sopenharmony_ci kfree(binding); 10162306a36Sopenharmony_cierr_binding_alloc: 10262306a36Sopenharmony_ci mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return err; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int mlxsw_sp_flow_block_unbind(struct mlxsw_sp *mlxsw_sp, 10862306a36Sopenharmony_ci struct mlxsw_sp_flow_block *block, 10962306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 11062306a36Sopenharmony_ci bool ingress) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct mlxsw_sp_flow_block_binding *binding; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci binding = mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress); 11562306a36Sopenharmony_ci if (!binding) 11662306a36Sopenharmony_ci return -ENOENT; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci list_del(&binding->list); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (ingress) 12162306a36Sopenharmony_ci block->ingress_binding_count--; 12262306a36Sopenharmony_ci else 12362306a36Sopenharmony_ci block->egress_binding_count--; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (mlxsw_sp_flow_block_ruleset_bound(block)) 12662306a36Sopenharmony_ci mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci kfree(binding); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int mlxsw_sp_flow_block_mall_cb(struct mlxsw_sp_flow_block *flow_block, 13662306a36Sopenharmony_ci struct tc_cls_matchall_offload *f) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_flow_block_mlxsw_sp(flow_block); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci switch (f->command) { 14162306a36Sopenharmony_ci case TC_CLSMATCHALL_REPLACE: 14262306a36Sopenharmony_ci return mlxsw_sp_mall_replace(mlxsw_sp, flow_block, f); 14362306a36Sopenharmony_ci case TC_CLSMATCHALL_DESTROY: 14462306a36Sopenharmony_ci mlxsw_sp_mall_destroy(flow_block, f); 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci default: 14762306a36Sopenharmony_ci return -EOPNOTSUPP; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int mlxsw_sp_flow_block_flower_cb(struct mlxsw_sp_flow_block *flow_block, 15262306a36Sopenharmony_ci struct flow_cls_offload *f) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_flow_block_mlxsw_sp(flow_block); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci switch (f->command) { 15762306a36Sopenharmony_ci case FLOW_CLS_REPLACE: 15862306a36Sopenharmony_ci return mlxsw_sp_flower_replace(mlxsw_sp, flow_block, f); 15962306a36Sopenharmony_ci case FLOW_CLS_DESTROY: 16062306a36Sopenharmony_ci mlxsw_sp_flower_destroy(mlxsw_sp, flow_block, f); 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci case FLOW_CLS_STATS: 16362306a36Sopenharmony_ci return mlxsw_sp_flower_stats(mlxsw_sp, flow_block, f); 16462306a36Sopenharmony_ci case FLOW_CLS_TMPLT_CREATE: 16562306a36Sopenharmony_ci return mlxsw_sp_flower_tmplt_create(mlxsw_sp, flow_block, f); 16662306a36Sopenharmony_ci case FLOW_CLS_TMPLT_DESTROY: 16762306a36Sopenharmony_ci mlxsw_sp_flower_tmplt_destroy(mlxsw_sp, flow_block, f); 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci default: 17062306a36Sopenharmony_ci return -EOPNOTSUPP; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int mlxsw_sp_flow_block_cb(enum tc_setup_type type, 17562306a36Sopenharmony_ci void *type_data, void *cb_priv) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct mlxsw_sp_flow_block *flow_block = cb_priv; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (mlxsw_sp_flow_block_disabled(flow_block)) 18062306a36Sopenharmony_ci return -EOPNOTSUPP; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci switch (type) { 18362306a36Sopenharmony_ci case TC_SETUP_CLSMATCHALL: 18462306a36Sopenharmony_ci return mlxsw_sp_flow_block_mall_cb(flow_block, type_data); 18562306a36Sopenharmony_ci case TC_SETUP_CLSFLOWER: 18662306a36Sopenharmony_ci return mlxsw_sp_flow_block_flower_cb(flow_block, type_data); 18762306a36Sopenharmony_ci default: 18862306a36Sopenharmony_ci return -EOPNOTSUPP; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void mlxsw_sp_tc_block_release(void *cb_priv) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct mlxsw_sp_flow_block *flow_block = cb_priv; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci mlxsw_sp_flow_block_destroy(flow_block); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic LIST_HEAD(mlxsw_sp_block_cb_list); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int mlxsw_sp_setup_tc_block_bind(struct mlxsw_sp_port *mlxsw_sp_port, 20262306a36Sopenharmony_ci struct flow_block_offload *f, 20362306a36Sopenharmony_ci bool ingress) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 20662306a36Sopenharmony_ci struct mlxsw_sp_flow_block *flow_block; 20762306a36Sopenharmony_ci struct flow_block_cb *block_cb; 20862306a36Sopenharmony_ci bool register_block = false; 20962306a36Sopenharmony_ci int err; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb, 21262306a36Sopenharmony_ci mlxsw_sp); 21362306a36Sopenharmony_ci if (!block_cb) { 21462306a36Sopenharmony_ci flow_block = mlxsw_sp_flow_block_create(mlxsw_sp, f->net); 21562306a36Sopenharmony_ci if (!flow_block) 21662306a36Sopenharmony_ci return -ENOMEM; 21762306a36Sopenharmony_ci block_cb = flow_block_cb_alloc(mlxsw_sp_flow_block_cb, 21862306a36Sopenharmony_ci mlxsw_sp, flow_block, 21962306a36Sopenharmony_ci mlxsw_sp_tc_block_release); 22062306a36Sopenharmony_ci if (IS_ERR(block_cb)) { 22162306a36Sopenharmony_ci mlxsw_sp_flow_block_destroy(flow_block); 22262306a36Sopenharmony_ci return PTR_ERR(block_cb); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci register_block = true; 22562306a36Sopenharmony_ci } else { 22662306a36Sopenharmony_ci flow_block = flow_block_cb_priv(block_cb); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci flow_block_cb_incref(block_cb); 22962306a36Sopenharmony_ci err = mlxsw_sp_flow_block_bind(mlxsw_sp, flow_block, 23062306a36Sopenharmony_ci mlxsw_sp_port, ingress, f->extack); 23162306a36Sopenharmony_ci if (err) 23262306a36Sopenharmony_ci goto err_block_bind; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (ingress) 23562306a36Sopenharmony_ci mlxsw_sp_port->ing_flow_block = flow_block; 23662306a36Sopenharmony_ci else 23762306a36Sopenharmony_ci mlxsw_sp_port->eg_flow_block = flow_block; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (register_block) { 24062306a36Sopenharmony_ci flow_block_cb_add(block_cb, f); 24162306a36Sopenharmony_ci list_add_tail(&block_cb->driver_list, &mlxsw_sp_block_cb_list); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cierr_block_bind: 24762306a36Sopenharmony_ci if (!flow_block_cb_decref(block_cb)) 24862306a36Sopenharmony_ci flow_block_cb_free(block_cb); 24962306a36Sopenharmony_ci return err; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void mlxsw_sp_setup_tc_block_unbind(struct mlxsw_sp_port *mlxsw_sp_port, 25362306a36Sopenharmony_ci struct flow_block_offload *f, 25462306a36Sopenharmony_ci bool ingress) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 25762306a36Sopenharmony_ci struct mlxsw_sp_flow_block *flow_block; 25862306a36Sopenharmony_ci struct flow_block_cb *block_cb; 25962306a36Sopenharmony_ci int err; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb, 26262306a36Sopenharmony_ci mlxsw_sp); 26362306a36Sopenharmony_ci if (!block_cb) 26462306a36Sopenharmony_ci return; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (ingress) 26762306a36Sopenharmony_ci mlxsw_sp_port->ing_flow_block = NULL; 26862306a36Sopenharmony_ci else 26962306a36Sopenharmony_ci mlxsw_sp_port->eg_flow_block = NULL; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci flow_block = flow_block_cb_priv(block_cb); 27262306a36Sopenharmony_ci err = mlxsw_sp_flow_block_unbind(mlxsw_sp, flow_block, 27362306a36Sopenharmony_ci mlxsw_sp_port, ingress); 27462306a36Sopenharmony_ci if (!err && !flow_block_cb_decref(block_cb)) { 27562306a36Sopenharmony_ci flow_block_cb_remove(block_cb, f); 27662306a36Sopenharmony_ci list_del(&block_cb->driver_list); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ciint mlxsw_sp_setup_tc_block_clsact(struct mlxsw_sp_port *mlxsw_sp_port, 28162306a36Sopenharmony_ci struct flow_block_offload *f, 28262306a36Sopenharmony_ci bool ingress) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci f->driver_block_list = &mlxsw_sp_block_cb_list; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci switch (f->command) { 28762306a36Sopenharmony_ci case FLOW_BLOCK_BIND: 28862306a36Sopenharmony_ci return mlxsw_sp_setup_tc_block_bind(mlxsw_sp_port, f, ingress); 28962306a36Sopenharmony_ci case FLOW_BLOCK_UNBIND: 29062306a36Sopenharmony_ci mlxsw_sp_setup_tc_block_unbind(mlxsw_sp_port, f, ingress); 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci default: 29362306a36Sopenharmony_ci return -EOPNOTSUPP; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci} 296