1d52238ebSJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2d52238ebSJiri Pirko /* Copyright (c) 2017-2020 Mellanox Technologies. All rights reserved */
3d52238ebSJiri Pirko
4d52238ebSJiri Pirko #include <linux/kernel.h>
5d52238ebSJiri Pirko #include <linux/slab.h>
6d52238ebSJiri Pirko #include <linux/errno.h>
7d52238ebSJiri Pirko #include <linux/list.h>
8d52238ebSJiri Pirko #include <net/net_namespace.h>
9d52238ebSJiri Pirko
10d52238ebSJiri Pirko #include "spectrum.h"
11d52238ebSJiri Pirko
12d52238ebSJiri Pirko struct mlxsw_sp_flow_block *
mlxsw_sp_flow_block_create(struct mlxsw_sp * mlxsw_sp,struct net * net)13d52238ebSJiri Pirko mlxsw_sp_flow_block_create(struct mlxsw_sp *mlxsw_sp, struct net *net)
14d52238ebSJiri Pirko {
15d52238ebSJiri Pirko struct mlxsw_sp_flow_block *block;
16d52238ebSJiri Pirko
17d52238ebSJiri Pirko block = kzalloc(sizeof(*block), GFP_KERNEL);
18d52238ebSJiri Pirko if (!block)
19d52238ebSJiri Pirko return NULL;
20d52238ebSJiri Pirko INIT_LIST_HEAD(&block->binding_list);
215a2939b9SJiri Pirko INIT_LIST_HEAD(&block->mall.list);
22d52238ebSJiri Pirko block->mlxsw_sp = mlxsw_sp;
23d52238ebSJiri Pirko block->net = net;
24d52238ebSJiri Pirko return block;
25d52238ebSJiri Pirko }
26d52238ebSJiri Pirko
mlxsw_sp_flow_block_destroy(struct mlxsw_sp_flow_block * block)27d52238ebSJiri Pirko void mlxsw_sp_flow_block_destroy(struct mlxsw_sp_flow_block *block)
28d52238ebSJiri Pirko {
29d52238ebSJiri Pirko WARN_ON(!list_empty(&block->binding_list));
30d52238ebSJiri Pirko kfree(block);
31d52238ebSJiri Pirko }
32d52238ebSJiri Pirko
33d52238ebSJiri Pirko static struct mlxsw_sp_flow_block_binding *
mlxsw_sp_flow_block_lookup(struct mlxsw_sp_flow_block * block,struct mlxsw_sp_port * mlxsw_sp_port,bool ingress)34d52238ebSJiri Pirko mlxsw_sp_flow_block_lookup(struct mlxsw_sp_flow_block *block,
35d52238ebSJiri Pirko struct mlxsw_sp_port *mlxsw_sp_port, bool ingress)
36d52238ebSJiri Pirko {
37d52238ebSJiri Pirko struct mlxsw_sp_flow_block_binding *binding;
38d52238ebSJiri Pirko
39d52238ebSJiri Pirko list_for_each_entry(binding, &block->binding_list, list)
40d52238ebSJiri Pirko if (binding->mlxsw_sp_port == mlxsw_sp_port &&
41d52238ebSJiri Pirko binding->ingress == ingress)
42d52238ebSJiri Pirko return binding;
43d52238ebSJiri Pirko return NULL;
44d52238ebSJiri Pirko }
45d52238ebSJiri Pirko
46d52238ebSJiri Pirko static bool
mlxsw_sp_flow_block_ruleset_bound(const struct mlxsw_sp_flow_block * block)47d52238ebSJiri Pirko mlxsw_sp_flow_block_ruleset_bound(const struct mlxsw_sp_flow_block *block)
48d52238ebSJiri Pirko {
49d52238ebSJiri Pirko return block->ruleset_zero;
50d52238ebSJiri Pirko }
51d52238ebSJiri Pirko
mlxsw_sp_flow_block_bind(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_flow_block * block,struct mlxsw_sp_port * mlxsw_sp_port,bool ingress,struct netlink_ext_ack * extack)5219f06771SJiri Pirko static int mlxsw_sp_flow_block_bind(struct mlxsw_sp *mlxsw_sp,
53d52238ebSJiri Pirko struct mlxsw_sp_flow_block *block,
54d52238ebSJiri Pirko struct mlxsw_sp_port *mlxsw_sp_port,
55d52238ebSJiri Pirko bool ingress,
56d52238ebSJiri Pirko struct netlink_ext_ack *extack)
57d52238ebSJiri Pirko {
58d52238ebSJiri Pirko struct mlxsw_sp_flow_block_binding *binding;
59d52238ebSJiri Pirko int err;
60d52238ebSJiri Pirko
61d52238ebSJiri Pirko if (WARN_ON(mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress)))
62d52238ebSJiri Pirko return -EEXIST;
63d52238ebSJiri Pirko
64d52238ebSJiri Pirko if (ingress && block->ingress_blocker_rule_count) {
65d52238ebSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to ingress because it contains unsupported rules");
66d52238ebSJiri Pirko return -EOPNOTSUPP;
67d52238ebSJiri Pirko }
68d52238ebSJiri Pirko
69d52238ebSJiri Pirko if (!ingress && block->egress_blocker_rule_count) {
70d52238ebSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to egress because it contains unsupported rules");
71d52238ebSJiri Pirko return -EOPNOTSUPP;
72d52238ebSJiri Pirko }
73d52238ebSJiri Pirko
74*6561df56SIdo Schimmel err = mlxsw_sp_mall_port_bind(block, mlxsw_sp_port, extack);
753c650136SJiri Pirko if (err)
763c650136SJiri Pirko return err;
773c650136SJiri Pirko
78d52238ebSJiri Pirko binding = kzalloc(sizeof(*binding), GFP_KERNEL);
793c650136SJiri Pirko if (!binding) {
803c650136SJiri Pirko err = -ENOMEM;
813c650136SJiri Pirko goto err_binding_alloc;
823c650136SJiri Pirko }
83d52238ebSJiri Pirko binding->mlxsw_sp_port = mlxsw_sp_port;
84d52238ebSJiri Pirko binding->ingress = ingress;
85d52238ebSJiri Pirko
86d52238ebSJiri Pirko if (mlxsw_sp_flow_block_ruleset_bound(block)) {
87d52238ebSJiri Pirko err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding);
88d52238ebSJiri Pirko if (err)
89d52238ebSJiri Pirko goto err_ruleset_bind;
90d52238ebSJiri Pirko }
91d52238ebSJiri Pirko
92d52238ebSJiri Pirko if (ingress)
93d52238ebSJiri Pirko block->ingress_binding_count++;
94d52238ebSJiri Pirko else
95d52238ebSJiri Pirko block->egress_binding_count++;
96d52238ebSJiri Pirko list_add(&binding->list, &block->binding_list);
97d52238ebSJiri Pirko return 0;
98d52238ebSJiri Pirko
99d52238ebSJiri Pirko err_ruleset_bind:
100d52238ebSJiri Pirko kfree(binding);
1013c650136SJiri Pirko err_binding_alloc:
1023c650136SJiri Pirko mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port);
1033c650136SJiri Pirko
104d52238ebSJiri Pirko return err;
105d52238ebSJiri Pirko }
106d52238ebSJiri Pirko
mlxsw_sp_flow_block_unbind(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_flow_block * block,struct mlxsw_sp_port * mlxsw_sp_port,bool ingress)10719f06771SJiri Pirko static int mlxsw_sp_flow_block_unbind(struct mlxsw_sp *mlxsw_sp,
108d52238ebSJiri Pirko struct mlxsw_sp_flow_block *block,
109d52238ebSJiri Pirko struct mlxsw_sp_port *mlxsw_sp_port,
110d52238ebSJiri Pirko bool ingress)
111d52238ebSJiri Pirko {
112d52238ebSJiri Pirko struct mlxsw_sp_flow_block_binding *binding;
113d52238ebSJiri Pirko
114d52238ebSJiri Pirko binding = mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress);
115d52238ebSJiri Pirko if (!binding)
116d52238ebSJiri Pirko return -ENOENT;
117d52238ebSJiri Pirko
118d52238ebSJiri Pirko list_del(&binding->list);
119d52238ebSJiri Pirko
120d52238ebSJiri Pirko if (ingress)
121d52238ebSJiri Pirko block->ingress_binding_count--;
122d52238ebSJiri Pirko else
123d52238ebSJiri Pirko block->egress_binding_count--;
124d52238ebSJiri Pirko
125d52238ebSJiri Pirko if (mlxsw_sp_flow_block_ruleset_bound(block))
126d52238ebSJiri Pirko mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
127d52238ebSJiri Pirko
128d52238ebSJiri Pirko kfree(binding);
1293c650136SJiri Pirko
1303c650136SJiri Pirko mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port);
1313c650136SJiri Pirko
132d52238ebSJiri Pirko return 0;
133d52238ebSJiri Pirko }
13419f06771SJiri Pirko
mlxsw_sp_flow_block_mall_cb(struct mlxsw_sp_flow_block * flow_block,struct tc_cls_matchall_offload * f)13519f06771SJiri Pirko static int mlxsw_sp_flow_block_mall_cb(struct mlxsw_sp_flow_block *flow_block,
13619f06771SJiri Pirko struct tc_cls_matchall_offload *f)
13719f06771SJiri Pirko {
13818346b70SJiri Pirko struct mlxsw_sp *mlxsw_sp = mlxsw_sp_flow_block_mlxsw_sp(flow_block);
13918346b70SJiri Pirko
14019f06771SJiri Pirko switch (f->command) {
14119f06771SJiri Pirko case TC_CLSMATCHALL_REPLACE:
14218346b70SJiri Pirko return mlxsw_sp_mall_replace(mlxsw_sp, flow_block, f);
14319f06771SJiri Pirko case TC_CLSMATCHALL_DESTROY:
14419f06771SJiri Pirko mlxsw_sp_mall_destroy(flow_block, f);
14519f06771SJiri Pirko return 0;
14619f06771SJiri Pirko default:
14719f06771SJiri Pirko return -EOPNOTSUPP;
14819f06771SJiri Pirko }
14919f06771SJiri Pirko }
15019f06771SJiri Pirko
mlxsw_sp_flow_block_flower_cb(struct mlxsw_sp_flow_block * flow_block,struct flow_cls_offload * f)15119f06771SJiri Pirko static int mlxsw_sp_flow_block_flower_cb(struct mlxsw_sp_flow_block *flow_block,
15219f06771SJiri Pirko struct flow_cls_offload *f)
15319f06771SJiri Pirko {
15419f06771SJiri Pirko struct mlxsw_sp *mlxsw_sp = mlxsw_sp_flow_block_mlxsw_sp(flow_block);
15519f06771SJiri Pirko
15619f06771SJiri Pirko switch (f->command) {
15719f06771SJiri Pirko case FLOW_CLS_REPLACE:
15819f06771SJiri Pirko return mlxsw_sp_flower_replace(mlxsw_sp, flow_block, f);
15919f06771SJiri Pirko case FLOW_CLS_DESTROY:
16019f06771SJiri Pirko mlxsw_sp_flower_destroy(mlxsw_sp, flow_block, f);
16119f06771SJiri Pirko return 0;
16219f06771SJiri Pirko case FLOW_CLS_STATS:
16319f06771SJiri Pirko return mlxsw_sp_flower_stats(mlxsw_sp, flow_block, f);
16419f06771SJiri Pirko case FLOW_CLS_TMPLT_CREATE:
16519f06771SJiri Pirko return mlxsw_sp_flower_tmplt_create(mlxsw_sp, flow_block, f);
16619f06771SJiri Pirko case FLOW_CLS_TMPLT_DESTROY:
16719f06771SJiri Pirko mlxsw_sp_flower_tmplt_destroy(mlxsw_sp, flow_block, f);
16819f06771SJiri Pirko return 0;
16919f06771SJiri Pirko default:
17019f06771SJiri Pirko return -EOPNOTSUPP;
17119f06771SJiri Pirko }
17219f06771SJiri Pirko }
17319f06771SJiri Pirko
mlxsw_sp_flow_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv)17419f06771SJiri Pirko static int mlxsw_sp_flow_block_cb(enum tc_setup_type type,
17519f06771SJiri Pirko void *type_data, void *cb_priv)
17619f06771SJiri Pirko {
17719f06771SJiri Pirko struct mlxsw_sp_flow_block *flow_block = cb_priv;
17819f06771SJiri Pirko
17919f06771SJiri Pirko if (mlxsw_sp_flow_block_disabled(flow_block))
18019f06771SJiri Pirko return -EOPNOTSUPP;
18119f06771SJiri Pirko
18219f06771SJiri Pirko switch (type) {
18319f06771SJiri Pirko case TC_SETUP_CLSMATCHALL:
18419f06771SJiri Pirko return mlxsw_sp_flow_block_mall_cb(flow_block, type_data);
18519f06771SJiri Pirko case TC_SETUP_CLSFLOWER:
18619f06771SJiri Pirko return mlxsw_sp_flow_block_flower_cb(flow_block, type_data);
18719f06771SJiri Pirko default:
18819f06771SJiri Pirko return -EOPNOTSUPP;
18919f06771SJiri Pirko }
19019f06771SJiri Pirko }
19119f06771SJiri Pirko
mlxsw_sp_tc_block_release(void * cb_priv)19219f06771SJiri Pirko static void mlxsw_sp_tc_block_release(void *cb_priv)
19319f06771SJiri Pirko {
19419f06771SJiri Pirko struct mlxsw_sp_flow_block *flow_block = cb_priv;
19519f06771SJiri Pirko
19619f06771SJiri Pirko mlxsw_sp_flow_block_destroy(flow_block);
19719f06771SJiri Pirko }
19819f06771SJiri Pirko
19919f06771SJiri Pirko static LIST_HEAD(mlxsw_sp_block_cb_list);
20019f06771SJiri Pirko
mlxsw_sp_setup_tc_block_bind(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f,bool ingress)20119f06771SJiri Pirko static int mlxsw_sp_setup_tc_block_bind(struct mlxsw_sp_port *mlxsw_sp_port,
20219f06771SJiri Pirko struct flow_block_offload *f,
20319f06771SJiri Pirko bool ingress)
20419f06771SJiri Pirko {
20519f06771SJiri Pirko struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
20619f06771SJiri Pirko struct mlxsw_sp_flow_block *flow_block;
20719f06771SJiri Pirko struct flow_block_cb *block_cb;
20819f06771SJiri Pirko bool register_block = false;
20919f06771SJiri Pirko int err;
21019f06771SJiri Pirko
21119f06771SJiri Pirko block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb,
21219f06771SJiri Pirko mlxsw_sp);
21319f06771SJiri Pirko if (!block_cb) {
21419f06771SJiri Pirko flow_block = mlxsw_sp_flow_block_create(mlxsw_sp, f->net);
21519f06771SJiri Pirko if (!flow_block)
21619f06771SJiri Pirko return -ENOMEM;
21719f06771SJiri Pirko block_cb = flow_block_cb_alloc(mlxsw_sp_flow_block_cb,
21819f06771SJiri Pirko mlxsw_sp, flow_block,
21919f06771SJiri Pirko mlxsw_sp_tc_block_release);
22019f06771SJiri Pirko if (IS_ERR(block_cb)) {
22119f06771SJiri Pirko mlxsw_sp_flow_block_destroy(flow_block);
2222c4950eaSPetr Machata return PTR_ERR(block_cb);
22319f06771SJiri Pirko }
22419f06771SJiri Pirko register_block = true;
22519f06771SJiri Pirko } else {
22619f06771SJiri Pirko flow_block = flow_block_cb_priv(block_cb);
22719f06771SJiri Pirko }
22819f06771SJiri Pirko flow_block_cb_incref(block_cb);
22919f06771SJiri Pirko err = mlxsw_sp_flow_block_bind(mlxsw_sp, flow_block,
23019f06771SJiri Pirko mlxsw_sp_port, ingress, f->extack);
23119f06771SJiri Pirko if (err)
23219f06771SJiri Pirko goto err_block_bind;
23319f06771SJiri Pirko
23419f06771SJiri Pirko if (ingress)
23519f06771SJiri Pirko mlxsw_sp_port->ing_flow_block = flow_block;
23619f06771SJiri Pirko else
23719f06771SJiri Pirko mlxsw_sp_port->eg_flow_block = flow_block;
23819f06771SJiri Pirko
23919f06771SJiri Pirko if (register_block) {
24019f06771SJiri Pirko flow_block_cb_add(block_cb, f);
24119f06771SJiri Pirko list_add_tail(&block_cb->driver_list, &mlxsw_sp_block_cb_list);
24219f06771SJiri Pirko }
24319f06771SJiri Pirko
24419f06771SJiri Pirko return 0;
24519f06771SJiri Pirko
24619f06771SJiri Pirko err_block_bind:
24719f06771SJiri Pirko if (!flow_block_cb_decref(block_cb))
24819f06771SJiri Pirko flow_block_cb_free(block_cb);
24919f06771SJiri Pirko return err;
25019f06771SJiri Pirko }
25119f06771SJiri Pirko
mlxsw_sp_setup_tc_block_unbind(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f,bool ingress)25219f06771SJiri Pirko static void mlxsw_sp_setup_tc_block_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
25319f06771SJiri Pirko struct flow_block_offload *f,
25419f06771SJiri Pirko bool ingress)
25519f06771SJiri Pirko {
25619f06771SJiri Pirko struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
25719f06771SJiri Pirko struct mlxsw_sp_flow_block *flow_block;
25819f06771SJiri Pirko struct flow_block_cb *block_cb;
25919f06771SJiri Pirko int err;
26019f06771SJiri Pirko
26119f06771SJiri Pirko block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb,
26219f06771SJiri Pirko mlxsw_sp);
26319f06771SJiri Pirko if (!block_cb)
26419f06771SJiri Pirko return;
26519f06771SJiri Pirko
26619f06771SJiri Pirko if (ingress)
26719f06771SJiri Pirko mlxsw_sp_port->ing_flow_block = NULL;
26819f06771SJiri Pirko else
26919f06771SJiri Pirko mlxsw_sp_port->eg_flow_block = NULL;
27019f06771SJiri Pirko
27119f06771SJiri Pirko flow_block = flow_block_cb_priv(block_cb);
27219f06771SJiri Pirko err = mlxsw_sp_flow_block_unbind(mlxsw_sp, flow_block,
27319f06771SJiri Pirko mlxsw_sp_port, ingress);
27419f06771SJiri Pirko if (!err && !flow_block_cb_decref(block_cb)) {
27519f06771SJiri Pirko flow_block_cb_remove(block_cb, f);
27619f06771SJiri Pirko list_del(&block_cb->driver_list);
27719f06771SJiri Pirko }
27819f06771SJiri Pirko }
27919f06771SJiri Pirko
mlxsw_sp_setup_tc_block_clsact(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f,bool ingress)280f7a439cbSPetr Machata int mlxsw_sp_setup_tc_block_clsact(struct mlxsw_sp_port *mlxsw_sp_port,
281f7a439cbSPetr Machata struct flow_block_offload *f,
282f7a439cbSPetr Machata bool ingress)
28319f06771SJiri Pirko {
28419f06771SJiri Pirko f->driver_block_list = &mlxsw_sp_block_cb_list;
28519f06771SJiri Pirko
28619f06771SJiri Pirko switch (f->command) {
28719f06771SJiri Pirko case FLOW_BLOCK_BIND:
28819f06771SJiri Pirko return mlxsw_sp_setup_tc_block_bind(mlxsw_sp_port, f, ingress);
28919f06771SJiri Pirko case FLOW_BLOCK_UNBIND:
29019f06771SJiri Pirko mlxsw_sp_setup_tc_block_unbind(mlxsw_sp_port, f, ingress);
29119f06771SJiri Pirko return 0;
29219f06771SJiri Pirko default:
29319f06771SJiri Pirko return -EOPNOTSUPP;
29419f06771SJiri Pirko }
29519f06771SJiri Pirko }
296