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