xref: /OK3568_Linux_fs/kernel/drivers/net/ethernet/mellanox/mlxsw/spectrum_flow.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2*4882a593Smuzhiyun /* Copyright (c) 2017-2020 Mellanox Technologies. All rights reserved */
3*4882a593Smuzhiyun 
4*4882a593Smuzhiyun #include <linux/kernel.h>
5*4882a593Smuzhiyun #include <linux/slab.h>
6*4882a593Smuzhiyun #include <linux/errno.h>
7*4882a593Smuzhiyun #include <linux/list.h>
8*4882a593Smuzhiyun #include <net/net_namespace.h>
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include "spectrum.h"
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun struct mlxsw_sp_flow_block *
mlxsw_sp_flow_block_create(struct mlxsw_sp * mlxsw_sp,struct net * net)13*4882a593Smuzhiyun mlxsw_sp_flow_block_create(struct mlxsw_sp *mlxsw_sp, struct net *net)
14*4882a593Smuzhiyun {
15*4882a593Smuzhiyun 	struct mlxsw_sp_flow_block *block;
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun 	block = kzalloc(sizeof(*block), GFP_KERNEL);
18*4882a593Smuzhiyun 	if (!block)
19*4882a593Smuzhiyun 		return NULL;
20*4882a593Smuzhiyun 	INIT_LIST_HEAD(&block->binding_list);
21*4882a593Smuzhiyun 	INIT_LIST_HEAD(&block->mall.list);
22*4882a593Smuzhiyun 	block->mlxsw_sp = mlxsw_sp;
23*4882a593Smuzhiyun 	block->net = net;
24*4882a593Smuzhiyun 	return block;
25*4882a593Smuzhiyun }
26*4882a593Smuzhiyun 
mlxsw_sp_flow_block_destroy(struct mlxsw_sp_flow_block * block)27*4882a593Smuzhiyun void mlxsw_sp_flow_block_destroy(struct mlxsw_sp_flow_block *block)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun 	WARN_ON(!list_empty(&block->binding_list));
30*4882a593Smuzhiyun 	kfree(block);
31*4882a593Smuzhiyun }
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 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)34*4882a593Smuzhiyun mlxsw_sp_flow_block_lookup(struct mlxsw_sp_flow_block *block,
35*4882a593Smuzhiyun 			   struct mlxsw_sp_port *mlxsw_sp_port, bool ingress)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun 	struct mlxsw_sp_flow_block_binding *binding;
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun 	list_for_each_entry(binding, &block->binding_list, list)
40*4882a593Smuzhiyun 		if (binding->mlxsw_sp_port == mlxsw_sp_port &&
41*4882a593Smuzhiyun 		    binding->ingress == ingress)
42*4882a593Smuzhiyun 			return binding;
43*4882a593Smuzhiyun 	return NULL;
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun static bool
mlxsw_sp_flow_block_ruleset_bound(const struct mlxsw_sp_flow_block * block)47*4882a593Smuzhiyun mlxsw_sp_flow_block_ruleset_bound(const struct mlxsw_sp_flow_block *block)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun 	return block->ruleset_zero;
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun 
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)52*4882a593Smuzhiyun static int mlxsw_sp_flow_block_bind(struct mlxsw_sp *mlxsw_sp,
53*4882a593Smuzhiyun 				    struct mlxsw_sp_flow_block *block,
54*4882a593Smuzhiyun 				    struct mlxsw_sp_port *mlxsw_sp_port,
55*4882a593Smuzhiyun 				    bool ingress,
56*4882a593Smuzhiyun 				    struct netlink_ext_ack *extack)
57*4882a593Smuzhiyun {
58*4882a593Smuzhiyun 	struct mlxsw_sp_flow_block_binding *binding;
59*4882a593Smuzhiyun 	int err;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	if (WARN_ON(mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress)))
62*4882a593Smuzhiyun 		return -EEXIST;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	if (ingress && block->ingress_blocker_rule_count) {
65*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to ingress because it contains unsupported rules");
66*4882a593Smuzhiyun 		return -EOPNOTSUPP;
67*4882a593Smuzhiyun 	}
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 	if (!ingress && block->egress_blocker_rule_count) {
70*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to egress because it contains unsupported rules");
71*4882a593Smuzhiyun 		return -EOPNOTSUPP;
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	err = mlxsw_sp_mall_port_bind(block, mlxsw_sp_port);
75*4882a593Smuzhiyun 	if (err)
76*4882a593Smuzhiyun 		return err;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	binding = kzalloc(sizeof(*binding), GFP_KERNEL);
79*4882a593Smuzhiyun 	if (!binding) {
80*4882a593Smuzhiyun 		err = -ENOMEM;
81*4882a593Smuzhiyun 		goto err_binding_alloc;
82*4882a593Smuzhiyun 	}
83*4882a593Smuzhiyun 	binding->mlxsw_sp_port = mlxsw_sp_port;
84*4882a593Smuzhiyun 	binding->ingress = ingress;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	if (mlxsw_sp_flow_block_ruleset_bound(block)) {
87*4882a593Smuzhiyun 		err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding);
88*4882a593Smuzhiyun 		if (err)
89*4882a593Smuzhiyun 			goto err_ruleset_bind;
90*4882a593Smuzhiyun 	}
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	if (ingress)
93*4882a593Smuzhiyun 		block->ingress_binding_count++;
94*4882a593Smuzhiyun 	else
95*4882a593Smuzhiyun 		block->egress_binding_count++;
96*4882a593Smuzhiyun 	list_add(&binding->list, &block->binding_list);
97*4882a593Smuzhiyun 	return 0;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun err_ruleset_bind:
100*4882a593Smuzhiyun 	kfree(binding);
101*4882a593Smuzhiyun err_binding_alloc:
102*4882a593Smuzhiyun 	mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port);
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	return err;
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun 
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)107*4882a593Smuzhiyun static int mlxsw_sp_flow_block_unbind(struct mlxsw_sp *mlxsw_sp,
108*4882a593Smuzhiyun 				      struct mlxsw_sp_flow_block *block,
109*4882a593Smuzhiyun 				      struct mlxsw_sp_port *mlxsw_sp_port,
110*4882a593Smuzhiyun 				      bool ingress)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	struct mlxsw_sp_flow_block_binding *binding;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	binding = mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress);
115*4882a593Smuzhiyun 	if (!binding)
116*4882a593Smuzhiyun 		return -ENOENT;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	list_del(&binding->list);
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	if (ingress)
121*4882a593Smuzhiyun 		block->ingress_binding_count--;
122*4882a593Smuzhiyun 	else
123*4882a593Smuzhiyun 		block->egress_binding_count--;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	if (mlxsw_sp_flow_block_ruleset_bound(block))
126*4882a593Smuzhiyun 		mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	kfree(binding);
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port);
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	return 0;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun 
mlxsw_sp_flow_block_mall_cb(struct mlxsw_sp_flow_block * flow_block,struct tc_cls_matchall_offload * f)135*4882a593Smuzhiyun static int mlxsw_sp_flow_block_mall_cb(struct mlxsw_sp_flow_block *flow_block,
136*4882a593Smuzhiyun 				       struct tc_cls_matchall_offload *f)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_flow_block_mlxsw_sp(flow_block);
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	switch (f->command) {
141*4882a593Smuzhiyun 	case TC_CLSMATCHALL_REPLACE:
142*4882a593Smuzhiyun 		return mlxsw_sp_mall_replace(mlxsw_sp, flow_block, f);
143*4882a593Smuzhiyun 	case TC_CLSMATCHALL_DESTROY:
144*4882a593Smuzhiyun 		mlxsw_sp_mall_destroy(flow_block, f);
145*4882a593Smuzhiyun 		return 0;
146*4882a593Smuzhiyun 	default:
147*4882a593Smuzhiyun 		return -EOPNOTSUPP;
148*4882a593Smuzhiyun 	}
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
mlxsw_sp_flow_block_flower_cb(struct mlxsw_sp_flow_block * flow_block,struct flow_cls_offload * f)151*4882a593Smuzhiyun static int mlxsw_sp_flow_block_flower_cb(struct mlxsw_sp_flow_block *flow_block,
152*4882a593Smuzhiyun 					 struct flow_cls_offload *f)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_flow_block_mlxsw_sp(flow_block);
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	switch (f->command) {
157*4882a593Smuzhiyun 	case FLOW_CLS_REPLACE:
158*4882a593Smuzhiyun 		return mlxsw_sp_flower_replace(mlxsw_sp, flow_block, f);
159*4882a593Smuzhiyun 	case FLOW_CLS_DESTROY:
160*4882a593Smuzhiyun 		mlxsw_sp_flower_destroy(mlxsw_sp, flow_block, f);
161*4882a593Smuzhiyun 		return 0;
162*4882a593Smuzhiyun 	case FLOW_CLS_STATS:
163*4882a593Smuzhiyun 		return mlxsw_sp_flower_stats(mlxsw_sp, flow_block, f);
164*4882a593Smuzhiyun 	case FLOW_CLS_TMPLT_CREATE:
165*4882a593Smuzhiyun 		return mlxsw_sp_flower_tmplt_create(mlxsw_sp, flow_block, f);
166*4882a593Smuzhiyun 	case FLOW_CLS_TMPLT_DESTROY:
167*4882a593Smuzhiyun 		mlxsw_sp_flower_tmplt_destroy(mlxsw_sp, flow_block, f);
168*4882a593Smuzhiyun 		return 0;
169*4882a593Smuzhiyun 	default:
170*4882a593Smuzhiyun 		return -EOPNOTSUPP;
171*4882a593Smuzhiyun 	}
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun 
mlxsw_sp_flow_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv)174*4882a593Smuzhiyun static int mlxsw_sp_flow_block_cb(enum tc_setup_type type,
175*4882a593Smuzhiyun 				  void *type_data, void *cb_priv)
176*4882a593Smuzhiyun {
177*4882a593Smuzhiyun 	struct mlxsw_sp_flow_block *flow_block = cb_priv;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	if (mlxsw_sp_flow_block_disabled(flow_block))
180*4882a593Smuzhiyun 		return -EOPNOTSUPP;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	switch (type) {
183*4882a593Smuzhiyun 	case TC_SETUP_CLSMATCHALL:
184*4882a593Smuzhiyun 		return mlxsw_sp_flow_block_mall_cb(flow_block, type_data);
185*4882a593Smuzhiyun 	case TC_SETUP_CLSFLOWER:
186*4882a593Smuzhiyun 		return mlxsw_sp_flow_block_flower_cb(flow_block, type_data);
187*4882a593Smuzhiyun 	default:
188*4882a593Smuzhiyun 		return -EOPNOTSUPP;
189*4882a593Smuzhiyun 	}
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun 
mlxsw_sp_tc_block_release(void * cb_priv)192*4882a593Smuzhiyun static void mlxsw_sp_tc_block_release(void *cb_priv)
193*4882a593Smuzhiyun {
194*4882a593Smuzhiyun 	struct mlxsw_sp_flow_block *flow_block = cb_priv;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	mlxsw_sp_flow_block_destroy(flow_block);
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun static LIST_HEAD(mlxsw_sp_block_cb_list);
200*4882a593Smuzhiyun 
mlxsw_sp_setup_tc_block_bind(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f,bool ingress)201*4882a593Smuzhiyun static int mlxsw_sp_setup_tc_block_bind(struct mlxsw_sp_port *mlxsw_sp_port,
202*4882a593Smuzhiyun 					struct flow_block_offload *f,
203*4882a593Smuzhiyun 					bool ingress)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
206*4882a593Smuzhiyun 	struct mlxsw_sp_flow_block *flow_block;
207*4882a593Smuzhiyun 	struct flow_block_cb *block_cb;
208*4882a593Smuzhiyun 	bool register_block = false;
209*4882a593Smuzhiyun 	int err;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb,
212*4882a593Smuzhiyun 					mlxsw_sp);
213*4882a593Smuzhiyun 	if (!block_cb) {
214*4882a593Smuzhiyun 		flow_block = mlxsw_sp_flow_block_create(mlxsw_sp, f->net);
215*4882a593Smuzhiyun 		if (!flow_block)
216*4882a593Smuzhiyun 			return -ENOMEM;
217*4882a593Smuzhiyun 		block_cb = flow_block_cb_alloc(mlxsw_sp_flow_block_cb,
218*4882a593Smuzhiyun 					       mlxsw_sp, flow_block,
219*4882a593Smuzhiyun 					       mlxsw_sp_tc_block_release);
220*4882a593Smuzhiyun 		if (IS_ERR(block_cb)) {
221*4882a593Smuzhiyun 			mlxsw_sp_flow_block_destroy(flow_block);
222*4882a593Smuzhiyun 			return PTR_ERR(block_cb);
223*4882a593Smuzhiyun 		}
224*4882a593Smuzhiyun 		register_block = true;
225*4882a593Smuzhiyun 	} else {
226*4882a593Smuzhiyun 		flow_block = flow_block_cb_priv(block_cb);
227*4882a593Smuzhiyun 	}
228*4882a593Smuzhiyun 	flow_block_cb_incref(block_cb);
229*4882a593Smuzhiyun 	err = mlxsw_sp_flow_block_bind(mlxsw_sp, flow_block,
230*4882a593Smuzhiyun 				       mlxsw_sp_port, ingress, f->extack);
231*4882a593Smuzhiyun 	if (err)
232*4882a593Smuzhiyun 		goto err_block_bind;
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	if (ingress)
235*4882a593Smuzhiyun 		mlxsw_sp_port->ing_flow_block = flow_block;
236*4882a593Smuzhiyun 	else
237*4882a593Smuzhiyun 		mlxsw_sp_port->eg_flow_block = flow_block;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	if (register_block) {
240*4882a593Smuzhiyun 		flow_block_cb_add(block_cb, f);
241*4882a593Smuzhiyun 		list_add_tail(&block_cb->driver_list, &mlxsw_sp_block_cb_list);
242*4882a593Smuzhiyun 	}
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	return 0;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun err_block_bind:
247*4882a593Smuzhiyun 	if (!flow_block_cb_decref(block_cb))
248*4882a593Smuzhiyun 		flow_block_cb_free(block_cb);
249*4882a593Smuzhiyun 	return err;
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun 
mlxsw_sp_setup_tc_block_unbind(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f,bool ingress)252*4882a593Smuzhiyun static void mlxsw_sp_setup_tc_block_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
253*4882a593Smuzhiyun 					   struct flow_block_offload *f,
254*4882a593Smuzhiyun 					   bool ingress)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
257*4882a593Smuzhiyun 	struct mlxsw_sp_flow_block *flow_block;
258*4882a593Smuzhiyun 	struct flow_block_cb *block_cb;
259*4882a593Smuzhiyun 	int err;
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb,
262*4882a593Smuzhiyun 					mlxsw_sp);
263*4882a593Smuzhiyun 	if (!block_cb)
264*4882a593Smuzhiyun 		return;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	if (ingress)
267*4882a593Smuzhiyun 		mlxsw_sp_port->ing_flow_block = NULL;
268*4882a593Smuzhiyun 	else
269*4882a593Smuzhiyun 		mlxsw_sp_port->eg_flow_block = NULL;
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	flow_block = flow_block_cb_priv(block_cb);
272*4882a593Smuzhiyun 	err = mlxsw_sp_flow_block_unbind(mlxsw_sp, flow_block,
273*4882a593Smuzhiyun 					 mlxsw_sp_port, ingress);
274*4882a593Smuzhiyun 	if (!err && !flow_block_cb_decref(block_cb)) {
275*4882a593Smuzhiyun 		flow_block_cb_remove(block_cb, f);
276*4882a593Smuzhiyun 		list_del(&block_cb->driver_list);
277*4882a593Smuzhiyun 	}
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun 
mlxsw_sp_setup_tc_block_clsact(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f,bool ingress)280*4882a593Smuzhiyun int mlxsw_sp_setup_tc_block_clsact(struct mlxsw_sp_port *mlxsw_sp_port,
281*4882a593Smuzhiyun 				   struct flow_block_offload *f,
282*4882a593Smuzhiyun 				   bool ingress)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun 	f->driver_block_list = &mlxsw_sp_block_cb_list;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	switch (f->command) {
287*4882a593Smuzhiyun 	case FLOW_BLOCK_BIND:
288*4882a593Smuzhiyun 		return mlxsw_sp_setup_tc_block_bind(mlxsw_sp_port, f, ingress);
289*4882a593Smuzhiyun 	case FLOW_BLOCK_UNBIND:
290*4882a593Smuzhiyun 		mlxsw_sp_setup_tc_block_unbind(mlxsw_sp_port, f, ingress);
291*4882a593Smuzhiyun 		return 0;
292*4882a593Smuzhiyun 	default:
293*4882a593Smuzhiyun 		return -EOPNOTSUPP;
294*4882a593Smuzhiyun 	}
295*4882a593Smuzhiyun }
296