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