18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2017-2020 Mellanox Technologies. All rights reserved */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/slab.h> 68c2ecf20Sopenharmony_ci#include <linux/errno.h> 78c2ecf20Sopenharmony_ci#include <linux/list.h> 88c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "spectrum.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistruct mlxsw_sp_flow_block * 138c2ecf20Sopenharmony_cimlxsw_sp_flow_block_create(struct mlxsw_sp *mlxsw_sp, struct net *net) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci struct mlxsw_sp_flow_block *block; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci block = kzalloc(sizeof(*block), GFP_KERNEL); 188c2ecf20Sopenharmony_ci if (!block) 198c2ecf20Sopenharmony_ci return NULL; 208c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&block->binding_list); 218c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&block->mall.list); 228c2ecf20Sopenharmony_ci block->mlxsw_sp = mlxsw_sp; 238c2ecf20Sopenharmony_ci block->net = net; 248c2ecf20Sopenharmony_ci return block; 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_civoid mlxsw_sp_flow_block_destroy(struct mlxsw_sp_flow_block *block) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&block->binding_list)); 308c2ecf20Sopenharmony_ci kfree(block); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct mlxsw_sp_flow_block_binding * 348c2ecf20Sopenharmony_cimlxsw_sp_flow_block_lookup(struct mlxsw_sp_flow_block *block, 358c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, bool ingress) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct mlxsw_sp_flow_block_binding *binding; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci list_for_each_entry(binding, &block->binding_list, list) 408c2ecf20Sopenharmony_ci if (binding->mlxsw_sp_port == mlxsw_sp_port && 418c2ecf20Sopenharmony_ci binding->ingress == ingress) 428c2ecf20Sopenharmony_ci return binding; 438c2ecf20Sopenharmony_ci return NULL; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic bool 478c2ecf20Sopenharmony_cimlxsw_sp_flow_block_ruleset_bound(const struct mlxsw_sp_flow_block *block) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci return block->ruleset_zero; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int mlxsw_sp_flow_block_bind(struct mlxsw_sp *mlxsw_sp, 538c2ecf20Sopenharmony_ci struct mlxsw_sp_flow_block *block, 548c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 558c2ecf20Sopenharmony_ci bool ingress, 568c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct mlxsw_sp_flow_block_binding *binding; 598c2ecf20Sopenharmony_ci int err; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (WARN_ON(mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress))) 628c2ecf20Sopenharmony_ci return -EEXIST; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (ingress && block->ingress_blocker_rule_count) { 658c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to ingress because it contains unsupported rules"); 668c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (!ingress && block->egress_blocker_rule_count) { 708c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to egress because it contains unsupported rules"); 718c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci err = mlxsw_sp_mall_port_bind(block, mlxsw_sp_port); 758c2ecf20Sopenharmony_ci if (err) 768c2ecf20Sopenharmony_ci return err; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci binding = kzalloc(sizeof(*binding), GFP_KERNEL); 798c2ecf20Sopenharmony_ci if (!binding) { 808c2ecf20Sopenharmony_ci err = -ENOMEM; 818c2ecf20Sopenharmony_ci goto err_binding_alloc; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci binding->mlxsw_sp_port = mlxsw_sp_port; 848c2ecf20Sopenharmony_ci binding->ingress = ingress; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (mlxsw_sp_flow_block_ruleset_bound(block)) { 878c2ecf20Sopenharmony_ci err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding); 888c2ecf20Sopenharmony_ci if (err) 898c2ecf20Sopenharmony_ci goto err_ruleset_bind; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (ingress) 938c2ecf20Sopenharmony_ci block->ingress_binding_count++; 948c2ecf20Sopenharmony_ci else 958c2ecf20Sopenharmony_ci block->egress_binding_count++; 968c2ecf20Sopenharmony_ci list_add(&binding->list, &block->binding_list); 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cierr_ruleset_bind: 1008c2ecf20Sopenharmony_ci kfree(binding); 1018c2ecf20Sopenharmony_cierr_binding_alloc: 1028c2ecf20Sopenharmony_ci mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return err; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int mlxsw_sp_flow_block_unbind(struct mlxsw_sp *mlxsw_sp, 1088c2ecf20Sopenharmony_ci struct mlxsw_sp_flow_block *block, 1098c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 1108c2ecf20Sopenharmony_ci bool ingress) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct mlxsw_sp_flow_block_binding *binding; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci binding = mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress); 1158c2ecf20Sopenharmony_ci if (!binding) 1168c2ecf20Sopenharmony_ci return -ENOENT; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci list_del(&binding->list); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (ingress) 1218c2ecf20Sopenharmony_ci block->ingress_binding_count--; 1228c2ecf20Sopenharmony_ci else 1238c2ecf20Sopenharmony_ci block->egress_binding_count--; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (mlxsw_sp_flow_block_ruleset_bound(block)) 1268c2ecf20Sopenharmony_ci mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci kfree(binding); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int mlxsw_sp_flow_block_mall_cb(struct mlxsw_sp_flow_block *flow_block, 1368c2ecf20Sopenharmony_ci struct tc_cls_matchall_offload *f) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_flow_block_mlxsw_sp(flow_block); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci switch (f->command) { 1418c2ecf20Sopenharmony_ci case TC_CLSMATCHALL_REPLACE: 1428c2ecf20Sopenharmony_ci return mlxsw_sp_mall_replace(mlxsw_sp, flow_block, f); 1438c2ecf20Sopenharmony_ci case TC_CLSMATCHALL_DESTROY: 1448c2ecf20Sopenharmony_ci mlxsw_sp_mall_destroy(flow_block, f); 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci default: 1478c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int mlxsw_sp_flow_block_flower_cb(struct mlxsw_sp_flow_block *flow_block, 1528c2ecf20Sopenharmony_ci struct flow_cls_offload *f) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_flow_block_mlxsw_sp(flow_block); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci switch (f->command) { 1578c2ecf20Sopenharmony_ci case FLOW_CLS_REPLACE: 1588c2ecf20Sopenharmony_ci return mlxsw_sp_flower_replace(mlxsw_sp, flow_block, f); 1598c2ecf20Sopenharmony_ci case FLOW_CLS_DESTROY: 1608c2ecf20Sopenharmony_ci mlxsw_sp_flower_destroy(mlxsw_sp, flow_block, f); 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci case FLOW_CLS_STATS: 1638c2ecf20Sopenharmony_ci return mlxsw_sp_flower_stats(mlxsw_sp, flow_block, f); 1648c2ecf20Sopenharmony_ci case FLOW_CLS_TMPLT_CREATE: 1658c2ecf20Sopenharmony_ci return mlxsw_sp_flower_tmplt_create(mlxsw_sp, flow_block, f); 1668c2ecf20Sopenharmony_ci case FLOW_CLS_TMPLT_DESTROY: 1678c2ecf20Sopenharmony_ci mlxsw_sp_flower_tmplt_destroy(mlxsw_sp, flow_block, f); 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci default: 1708c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int mlxsw_sp_flow_block_cb(enum tc_setup_type type, 1758c2ecf20Sopenharmony_ci void *type_data, void *cb_priv) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct mlxsw_sp_flow_block *flow_block = cb_priv; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (mlxsw_sp_flow_block_disabled(flow_block)) 1808c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci switch (type) { 1838c2ecf20Sopenharmony_ci case TC_SETUP_CLSMATCHALL: 1848c2ecf20Sopenharmony_ci return mlxsw_sp_flow_block_mall_cb(flow_block, type_data); 1858c2ecf20Sopenharmony_ci case TC_SETUP_CLSFLOWER: 1868c2ecf20Sopenharmony_ci return mlxsw_sp_flow_block_flower_cb(flow_block, type_data); 1878c2ecf20Sopenharmony_ci default: 1888c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void mlxsw_sp_tc_block_release(void *cb_priv) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct mlxsw_sp_flow_block *flow_block = cb_priv; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci mlxsw_sp_flow_block_destroy(flow_block); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic LIST_HEAD(mlxsw_sp_block_cb_list); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int mlxsw_sp_setup_tc_block_bind(struct mlxsw_sp_port *mlxsw_sp_port, 2028c2ecf20Sopenharmony_ci struct flow_block_offload *f, 2038c2ecf20Sopenharmony_ci bool ingress) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 2068c2ecf20Sopenharmony_ci struct mlxsw_sp_flow_block *flow_block; 2078c2ecf20Sopenharmony_ci struct flow_block_cb *block_cb; 2088c2ecf20Sopenharmony_ci bool register_block = false; 2098c2ecf20Sopenharmony_ci int err; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb, 2128c2ecf20Sopenharmony_ci mlxsw_sp); 2138c2ecf20Sopenharmony_ci if (!block_cb) { 2148c2ecf20Sopenharmony_ci flow_block = mlxsw_sp_flow_block_create(mlxsw_sp, f->net); 2158c2ecf20Sopenharmony_ci if (!flow_block) 2168c2ecf20Sopenharmony_ci return -ENOMEM; 2178c2ecf20Sopenharmony_ci block_cb = flow_block_cb_alloc(mlxsw_sp_flow_block_cb, 2188c2ecf20Sopenharmony_ci mlxsw_sp, flow_block, 2198c2ecf20Sopenharmony_ci mlxsw_sp_tc_block_release); 2208c2ecf20Sopenharmony_ci if (IS_ERR(block_cb)) { 2218c2ecf20Sopenharmony_ci mlxsw_sp_flow_block_destroy(flow_block); 2228c2ecf20Sopenharmony_ci return PTR_ERR(block_cb); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci register_block = true; 2258c2ecf20Sopenharmony_ci } else { 2268c2ecf20Sopenharmony_ci flow_block = flow_block_cb_priv(block_cb); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci flow_block_cb_incref(block_cb); 2298c2ecf20Sopenharmony_ci err = mlxsw_sp_flow_block_bind(mlxsw_sp, flow_block, 2308c2ecf20Sopenharmony_ci mlxsw_sp_port, ingress, f->extack); 2318c2ecf20Sopenharmony_ci if (err) 2328c2ecf20Sopenharmony_ci goto err_block_bind; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (ingress) 2358c2ecf20Sopenharmony_ci mlxsw_sp_port->ing_flow_block = flow_block; 2368c2ecf20Sopenharmony_ci else 2378c2ecf20Sopenharmony_ci mlxsw_sp_port->eg_flow_block = flow_block; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (register_block) { 2408c2ecf20Sopenharmony_ci flow_block_cb_add(block_cb, f); 2418c2ecf20Sopenharmony_ci list_add_tail(&block_cb->driver_list, &mlxsw_sp_block_cb_list); 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cierr_block_bind: 2478c2ecf20Sopenharmony_ci if (!flow_block_cb_decref(block_cb)) 2488c2ecf20Sopenharmony_ci flow_block_cb_free(block_cb); 2498c2ecf20Sopenharmony_ci return err; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic void mlxsw_sp_setup_tc_block_unbind(struct mlxsw_sp_port *mlxsw_sp_port, 2538c2ecf20Sopenharmony_ci struct flow_block_offload *f, 2548c2ecf20Sopenharmony_ci bool ingress) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 2578c2ecf20Sopenharmony_ci struct mlxsw_sp_flow_block *flow_block; 2588c2ecf20Sopenharmony_ci struct flow_block_cb *block_cb; 2598c2ecf20Sopenharmony_ci int err; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb, 2628c2ecf20Sopenharmony_ci mlxsw_sp); 2638c2ecf20Sopenharmony_ci if (!block_cb) 2648c2ecf20Sopenharmony_ci return; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (ingress) 2678c2ecf20Sopenharmony_ci mlxsw_sp_port->ing_flow_block = NULL; 2688c2ecf20Sopenharmony_ci else 2698c2ecf20Sopenharmony_ci mlxsw_sp_port->eg_flow_block = NULL; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci flow_block = flow_block_cb_priv(block_cb); 2728c2ecf20Sopenharmony_ci err = mlxsw_sp_flow_block_unbind(mlxsw_sp, flow_block, 2738c2ecf20Sopenharmony_ci mlxsw_sp_port, ingress); 2748c2ecf20Sopenharmony_ci if (!err && !flow_block_cb_decref(block_cb)) { 2758c2ecf20Sopenharmony_ci flow_block_cb_remove(block_cb, f); 2768c2ecf20Sopenharmony_ci list_del(&block_cb->driver_list); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ciint mlxsw_sp_setup_tc_block_clsact(struct mlxsw_sp_port *mlxsw_sp_port, 2818c2ecf20Sopenharmony_ci struct flow_block_offload *f, 2828c2ecf20Sopenharmony_ci bool ingress) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci f->driver_block_list = &mlxsw_sp_block_cb_list; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci switch (f->command) { 2878c2ecf20Sopenharmony_ci case FLOW_BLOCK_BIND: 2888c2ecf20Sopenharmony_ci return mlxsw_sp_setup_tc_block_bind(mlxsw_sp_port, f, ingress); 2898c2ecf20Sopenharmony_ci case FLOW_BLOCK_UNBIND: 2908c2ecf20Sopenharmony_ci mlxsw_sp_setup_tc_block_unbind(mlxsw_sp_port, f, ingress); 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci default: 2938c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci} 296