xref: /openbmc/linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_matchall.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
1d7fcc986SJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2d7fcc986SJiri Pirko /* Copyright (c) 2017-2020 Mellanox Technologies. All rights reserved */
3d7fcc986SJiri Pirko 
4d7fcc986SJiri Pirko #include <linux/kernel.h>
5d7fcc986SJiri Pirko #include <linux/errno.h>
6d7fcc986SJiri Pirko #include <linux/netdevice.h>
7d7fcc986SJiri Pirko #include <net/flow_offload.h>
8d7fcc986SJiri Pirko 
9d7fcc986SJiri Pirko #include "spectrum.h"
10d7fcc986SJiri Pirko #include "spectrum_span.h"
11d7fcc986SJiri Pirko #include "reg.h"
12d7fcc986SJiri Pirko 
13d7fcc986SJiri Pirko static struct mlxsw_sp_mall_entry *
mlxsw_sp_mall_entry_find(struct mlxsw_sp_flow_block * block,unsigned long cookie)143c650136SJiri Pirko mlxsw_sp_mall_entry_find(struct mlxsw_sp_flow_block *block, unsigned long cookie)
15d7fcc986SJiri Pirko {
16d7fcc986SJiri Pirko 	struct mlxsw_sp_mall_entry *mall_entry;
17d7fcc986SJiri Pirko 
185a2939b9SJiri Pirko 	list_for_each_entry(mall_entry, &block->mall.list, list)
19d7fcc986SJiri Pirko 		if (mall_entry->cookie == cookie)
20d7fcc986SJiri Pirko 			return mall_entry;
21d7fcc986SJiri Pirko 
22d7fcc986SJiri Pirko 	return NULL;
23d7fcc986SJiri Pirko }
24d7fcc986SJiri Pirko 
25d7fcc986SJiri Pirko static int
mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_mall_entry * mall_entry,struct netlink_ext_ack * extack)26d7fcc986SJiri Pirko mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port,
276561df56SIdo Schimmel 			      struct mlxsw_sp_mall_entry *mall_entry,
286561df56SIdo Schimmel 			      struct netlink_ext_ack *extack)
29d7fcc986SJiri Pirko {
30c1d7845dSIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
314039504eSIdo Schimmel 	struct mlxsw_sp_span_agent_parms agent_parms = {};
32c1d7845dSIdo Schimmel 	struct mlxsw_sp_span_trigger_parms parms;
33c1d7845dSIdo Schimmel 	enum mlxsw_sp_span_trigger trigger;
34c1d7845dSIdo Schimmel 	int err;
35d7fcc986SJiri Pirko 
36780ba878SJiri Pirko 	if (!mall_entry->mirror.to_dev) {
376561df56SIdo Schimmel 		NL_SET_ERR_MSG(extack, "Could not find requested device");
38d7fcc986SJiri Pirko 		return -EINVAL;
39d7fcc986SJiri Pirko 	}
40d7fcc986SJiri Pirko 
41a120ecc3SIdo Schimmel 	agent_parms.to_dev = mall_entry->mirror.to_dev;
42a120ecc3SIdo Schimmel 	err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->mirror.span_id,
43a120ecc3SIdo Schimmel 				      &agent_parms);
446561df56SIdo Schimmel 	if (err) {
456561df56SIdo Schimmel 		NL_SET_ERR_MSG(extack, "Failed to get SPAN agent");
46c1d7845dSIdo Schimmel 		return err;
476561df56SIdo Schimmel 	}
48c1d7845dSIdo Schimmel 
49c1d7845dSIdo Schimmel 	err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port,
50c1d7845dSIdo Schimmel 					      mall_entry->ingress);
516561df56SIdo Schimmel 	if (err) {
526561df56SIdo Schimmel 		NL_SET_ERR_MSG(extack, "Failed to get analyzed port");
53c1d7845dSIdo Schimmel 		goto err_analyzed_port_get;
546561df56SIdo Schimmel 	}
55c1d7845dSIdo Schimmel 
56c1d7845dSIdo Schimmel 	trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
57c1d7845dSIdo Schimmel 					MLXSW_SP_SPAN_TRIGGER_EGRESS;
58c1d7845dSIdo Schimmel 	parms.span_id = mall_entry->mirror.span_id;
592dcbd920SIdo Schimmel 	parms.probability_rate = 1;
60c1d7845dSIdo Schimmel 	err = mlxsw_sp_span_agent_bind(mlxsw_sp, trigger, mlxsw_sp_port,
61c1d7845dSIdo Schimmel 				       &parms);
626561df56SIdo Schimmel 	if (err) {
636561df56SIdo Schimmel 		NL_SET_ERR_MSG(extack, "Failed to bind SPAN agent");
64c1d7845dSIdo Schimmel 		goto err_agent_bind;
656561df56SIdo Schimmel 	}
66c1d7845dSIdo Schimmel 
67c1d7845dSIdo Schimmel 	return 0;
68c1d7845dSIdo Schimmel 
69c1d7845dSIdo Schimmel err_agent_bind:
70c1d7845dSIdo Schimmel 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
71c1d7845dSIdo Schimmel err_analyzed_port_get:
72c1d7845dSIdo Schimmel 	mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->mirror.span_id);
73c1d7845dSIdo Schimmel 	return err;
74d7fcc986SJiri Pirko }
75d7fcc986SJiri Pirko 
76d7fcc986SJiri Pirko static void
mlxsw_sp_mall_port_mirror_del(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_mall_entry * mall_entry)77d7fcc986SJiri Pirko mlxsw_sp_mall_port_mirror_del(struct mlxsw_sp_port *mlxsw_sp_port,
78780ba878SJiri Pirko 			      struct mlxsw_sp_mall_entry *mall_entry)
79d7fcc986SJiri Pirko {
80c1d7845dSIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
81c1d7845dSIdo Schimmel 	struct mlxsw_sp_span_trigger_parms parms;
82c1d7845dSIdo Schimmel 	enum mlxsw_sp_span_trigger trigger;
83d7fcc986SJiri Pirko 
84c1d7845dSIdo Schimmel 	trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
85c1d7845dSIdo Schimmel 					MLXSW_SP_SPAN_TRIGGER_EGRESS;
86c1d7845dSIdo Schimmel 	parms.span_id = mall_entry->mirror.span_id;
87c1d7845dSIdo Schimmel 	mlxsw_sp_span_agent_unbind(mlxsw_sp, trigger, mlxsw_sp_port, &parms);
88c1d7845dSIdo Schimmel 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
89c1d7845dSIdo Schimmel 	mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->mirror.span_id);
90d7fcc986SJiri Pirko }
91d7fcc986SJiri Pirko 
mlxsw_sp_mall_port_sample_set(struct mlxsw_sp_port * mlxsw_sp_port,bool enable,u32 rate)92d7fcc986SJiri Pirko static int mlxsw_sp_mall_port_sample_set(struct mlxsw_sp_port *mlxsw_sp_port,
93d7fcc986SJiri Pirko 					 bool enable, u32 rate)
94d7fcc986SJiri Pirko {
95d7fcc986SJiri Pirko 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
96d7fcc986SJiri Pirko 	char mpsc_pl[MLXSW_REG_MPSC_LEN];
97d7fcc986SJiri Pirko 
98d7fcc986SJiri Pirko 	mlxsw_reg_mpsc_pack(mpsc_pl, mlxsw_sp_port->local_port, enable, rate);
99d7fcc986SJiri Pirko 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpsc), mpsc_pl);
100d7fcc986SJiri Pirko }
101d7fcc986SJiri Pirko 
102d7fcc986SJiri Pirko static int
mlxsw_sp_mall_port_sample_add(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_mall_entry * mall_entry,struct netlink_ext_ack * extack)103d7fcc986SJiri Pirko mlxsw_sp_mall_port_sample_add(struct mlxsw_sp_port *mlxsw_sp_port,
1046561df56SIdo Schimmel 			      struct mlxsw_sp_mall_entry *mall_entry,
1056561df56SIdo Schimmel 			      struct netlink_ext_ack *extack)
106d7fcc986SJiri Pirko {
10720afb9bcSIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
10890f53c53SIdo Schimmel 	struct mlxsw_sp_sample_trigger trigger;
109d7fcc986SJiri Pirko 	int err;
110d7fcc986SJiri Pirko 
11190f53c53SIdo Schimmel 	if (mall_entry->ingress)
11290f53c53SIdo Schimmel 		trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS;
11390f53c53SIdo Schimmel 	else
11490f53c53SIdo Schimmel 		trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS;
11590f53c53SIdo Schimmel 	trigger.local_port = mlxsw_sp_port->local_port;
11690f53c53SIdo Schimmel 	err = mlxsw_sp_sample_trigger_params_set(mlxsw_sp, &trigger,
11790f53c53SIdo Schimmel 						 &mall_entry->sample.params,
11890f53c53SIdo Schimmel 						 extack);
11990f53c53SIdo Schimmel 	if (err)
12090f53c53SIdo Schimmel 		return err;
121d7fcc986SJiri Pirko 
12220afb9bcSIdo Schimmel 	err = mlxsw_sp->mall_ops->sample_add(mlxsw_sp, mlxsw_sp_port,
123e09a5955SIdo Schimmel 					     mall_entry, extack);
124d7fcc986SJiri Pirko 	if (err)
125d7fcc986SJiri Pirko 		goto err_port_sample_set;
126d7fcc986SJiri Pirko 	return 0;
127d7fcc986SJiri Pirko 
128d7fcc986SJiri Pirko err_port_sample_set:
12990f53c53SIdo Schimmel 	mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
130d7fcc986SJiri Pirko 	return err;
131d7fcc986SJiri Pirko }
132d7fcc986SJiri Pirko 
133d7fcc986SJiri Pirko static void
mlxsw_sp_mall_port_sample_del(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_mall_entry * mall_entry)134e09a5955SIdo Schimmel mlxsw_sp_mall_port_sample_del(struct mlxsw_sp_port *mlxsw_sp_port,
135e09a5955SIdo Schimmel 			      struct mlxsw_sp_mall_entry *mall_entry)
136d7fcc986SJiri Pirko {
13720afb9bcSIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
13890f53c53SIdo Schimmel 	struct mlxsw_sp_sample_trigger trigger;
13920afb9bcSIdo Schimmel 
14090f53c53SIdo Schimmel 	if (mall_entry->ingress)
14190f53c53SIdo Schimmel 		trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS;
14290f53c53SIdo Schimmel 	else
14390f53c53SIdo Schimmel 		trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS;
14490f53c53SIdo Schimmel 	trigger.local_port = mlxsw_sp_port->local_port;
145d7fcc986SJiri Pirko 
146e09a5955SIdo Schimmel 	mlxsw_sp->mall_ops->sample_del(mlxsw_sp, mlxsw_sp_port, mall_entry);
14790f53c53SIdo Schimmel 	mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
148d7fcc986SJiri Pirko }
149d7fcc986SJiri Pirko 
150dd0fbc89SJiri Pirko static int
mlxsw_sp_mall_port_rule_add(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_mall_entry * mall_entry,struct netlink_ext_ack * extack)151dd0fbc89SJiri Pirko mlxsw_sp_mall_port_rule_add(struct mlxsw_sp_port *mlxsw_sp_port,
1526561df56SIdo Schimmel 			    struct mlxsw_sp_mall_entry *mall_entry,
1536561df56SIdo Schimmel 			    struct netlink_ext_ack *extack)
154dd0fbc89SJiri Pirko {
155dd0fbc89SJiri Pirko 	switch (mall_entry->type) {
156dd0fbc89SJiri Pirko 	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1576561df56SIdo Schimmel 		return mlxsw_sp_mall_port_mirror_add(mlxsw_sp_port, mall_entry,
1586561df56SIdo Schimmel 						     extack);
159dd0fbc89SJiri Pirko 	case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE:
1606561df56SIdo Schimmel 		return mlxsw_sp_mall_port_sample_add(mlxsw_sp_port, mall_entry,
1616561df56SIdo Schimmel 						     extack);
162dd0fbc89SJiri Pirko 	default:
163dd0fbc89SJiri Pirko 		WARN_ON(1);
164dd0fbc89SJiri Pirko 		return -EINVAL;
165dd0fbc89SJiri Pirko 	}
166dd0fbc89SJiri Pirko }
167dd0fbc89SJiri Pirko 
168dd0fbc89SJiri Pirko static void
mlxsw_sp_mall_port_rule_del(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_mall_entry * mall_entry)169dd0fbc89SJiri Pirko mlxsw_sp_mall_port_rule_del(struct mlxsw_sp_port *mlxsw_sp_port,
170dd0fbc89SJiri Pirko 			    struct mlxsw_sp_mall_entry *mall_entry)
171dd0fbc89SJiri Pirko {
172dd0fbc89SJiri Pirko 	switch (mall_entry->type) {
173dd0fbc89SJiri Pirko 	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
174dd0fbc89SJiri Pirko 		mlxsw_sp_mall_port_mirror_del(mlxsw_sp_port, mall_entry);
175dd0fbc89SJiri Pirko 		break;
176dd0fbc89SJiri Pirko 	case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE:
177e09a5955SIdo Schimmel 		mlxsw_sp_mall_port_sample_del(mlxsw_sp_port, mall_entry);
178dd0fbc89SJiri Pirko 		break;
179dd0fbc89SJiri Pirko 	default:
180dd0fbc89SJiri Pirko 		WARN_ON(1);
181dd0fbc89SJiri Pirko 	}
182dd0fbc89SJiri Pirko }
183dd0fbc89SJiri Pirko 
mlxsw_sp_mall_prio_update(struct mlxsw_sp_flow_block * block)184aed65285SJiri Pirko static void mlxsw_sp_mall_prio_update(struct mlxsw_sp_flow_block *block)
185aed65285SJiri Pirko {
186aed65285SJiri Pirko 	struct mlxsw_sp_mall_entry *mall_entry;
187aed65285SJiri Pirko 
188aed65285SJiri Pirko 	if (list_empty(&block->mall.list))
189aed65285SJiri Pirko 		return;
190aed65285SJiri Pirko 	block->mall.min_prio = UINT_MAX;
191aed65285SJiri Pirko 	block->mall.max_prio = 0;
192aed65285SJiri Pirko 	list_for_each_entry(mall_entry, &block->mall.list, list) {
193aed65285SJiri Pirko 		if (mall_entry->priority < block->mall.min_prio)
194aed65285SJiri Pirko 			block->mall.min_prio = mall_entry->priority;
195aed65285SJiri Pirko 		if (mall_entry->priority > block->mall.max_prio)
196aed65285SJiri Pirko 			block->mall.max_prio = mall_entry->priority;
197aed65285SJiri Pirko 	}
198aed65285SJiri Pirko }
199aed65285SJiri Pirko 
mlxsw_sp_mall_replace(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_flow_block * block,struct tc_cls_matchall_offload * f)20018346b70SJiri Pirko int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp,
20118346b70SJiri Pirko 			  struct mlxsw_sp_flow_block *block,
2023c650136SJiri Pirko 			  struct tc_cls_matchall_offload *f)
203d7fcc986SJiri Pirko {
2043c650136SJiri Pirko 	struct mlxsw_sp_flow_block_binding *binding;
205d7fcc986SJiri Pirko 	struct mlxsw_sp_mall_entry *mall_entry;
206d7fcc986SJiri Pirko 	__be16 protocol = f->common.protocol;
207d7fcc986SJiri Pirko 	struct flow_action_entry *act;
20818346b70SJiri Pirko 	unsigned int flower_min_prio;
20918346b70SJiri Pirko 	unsigned int flower_max_prio;
21018346b70SJiri Pirko 	bool flower_prio_valid;
211d7fcc986SJiri Pirko 	int err;
212d7fcc986SJiri Pirko 
213d7fcc986SJiri Pirko 	if (!flow_offload_has_one_action(&f->rule->action)) {
2143c650136SJiri Pirko 		NL_SET_ERR_MSG(f->common.extack, "Only singular actions are supported");
2153c650136SJiri Pirko 		return -EOPNOTSUPP;
2163c650136SJiri Pirko 	}
2173c650136SJiri Pirko 
2183c650136SJiri Pirko 	if (f->common.chain_index) {
2193c650136SJiri Pirko 		NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported");
2203c650136SJiri Pirko 		return -EOPNOTSUPP;
2213c650136SJiri Pirko 	}
2223c650136SJiri Pirko 
2233c650136SJiri Pirko 	if (mlxsw_sp_flow_block_is_mixed_bound(block)) {
2243c650136SJiri Pirko 		NL_SET_ERR_MSG(f->common.extack, "Only not mixed bound blocks are supported");
225d7fcc986SJiri Pirko 		return -EOPNOTSUPP;
226d7fcc986SJiri Pirko 	}
227d7fcc986SJiri Pirko 
22818346b70SJiri Pirko 	err = mlxsw_sp_flower_prio_get(mlxsw_sp, block, f->common.chain_index,
22918346b70SJiri Pirko 				       &flower_min_prio, &flower_max_prio);
23018346b70SJiri Pirko 	if (err) {
23118346b70SJiri Pirko 		if (err != -ENOENT) {
23218346b70SJiri Pirko 			NL_SET_ERR_MSG(f->common.extack, "Failed to get flower priorities");
23318346b70SJiri Pirko 			return err;
23418346b70SJiri Pirko 		}
23518346b70SJiri Pirko 		flower_prio_valid = false;
23618346b70SJiri Pirko 		/* No flower filters are installed in specified chain. */
23718346b70SJiri Pirko 	} else {
23818346b70SJiri Pirko 		flower_prio_valid = true;
23918346b70SJiri Pirko 	}
24018346b70SJiri Pirko 
2414947e730SIdo Schimmel 	if (protocol != htons(ETH_P_ALL)) {
2424947e730SIdo Schimmel 		NL_SET_ERR_MSG(f->common.extack, "matchall rules only supported with 'all' protocol");
2434947e730SIdo Schimmel 		return -EOPNOTSUPP;
2444947e730SIdo Schimmel 	}
2454947e730SIdo Schimmel 
246d7fcc986SJiri Pirko 	mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
247d7fcc986SJiri Pirko 	if (!mall_entry)
248d7fcc986SJiri Pirko 		return -ENOMEM;
249d7fcc986SJiri Pirko 	mall_entry->cookie = f->cookie;
250aed65285SJiri Pirko 	mall_entry->priority = f->common.prio;
2513c650136SJiri Pirko 	mall_entry->ingress = mlxsw_sp_flow_block_is_ingress_bound(block);
252d7fcc986SJiri Pirko 
25318346b70SJiri Pirko 	if (flower_prio_valid && mall_entry->ingress &&
25418346b70SJiri Pirko 	    mall_entry->priority >= flower_min_prio) {
25518346b70SJiri Pirko 		NL_SET_ERR_MSG(f->common.extack, "Failed to add behind existing flower rules");
25618346b70SJiri Pirko 		err = -EOPNOTSUPP;
25718346b70SJiri Pirko 		goto errout;
25818346b70SJiri Pirko 	}
25918346b70SJiri Pirko 	if (flower_prio_valid && !mall_entry->ingress &&
26018346b70SJiri Pirko 	    mall_entry->priority <= flower_max_prio) {
26118346b70SJiri Pirko 		NL_SET_ERR_MSG(f->common.extack, "Failed to add in front of existing flower rules");
26218346b70SJiri Pirko 		err = -EOPNOTSUPP;
26318346b70SJiri Pirko 		goto errout;
26418346b70SJiri Pirko 	}
265*b2430304SIdo Schimmel 
266*b2430304SIdo Schimmel 	act = &f->rule->action.entries[0];
267*b2430304SIdo Schimmel 
268*b2430304SIdo Schimmel 	switch (act->id) {
269*b2430304SIdo Schimmel 	case FLOW_ACTION_MIRRED:
270d7fcc986SJiri Pirko 		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR;
271780ba878SJiri Pirko 		mall_entry->mirror.to_dev = act->dev;
27250401f29SIdo Schimmel 		break;
27350401f29SIdo Schimmel 	case FLOW_ACTION_SAMPLE:
274d7fcc986SJiri Pirko 		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_SAMPLE;
27590f53c53SIdo Schimmel 		mall_entry->sample.params.psample_group = act->sample.psample_group;
27690f53c53SIdo Schimmel 		mall_entry->sample.params.truncate = act->sample.truncate;
27790f53c53SIdo Schimmel 		mall_entry->sample.params.trunc_size = act->sample.trunc_size;
27890f53c53SIdo Schimmel 		mall_entry->sample.params.rate = act->sample.rate;
27950401f29SIdo Schimmel 		break;
28050401f29SIdo Schimmel 	default:
281d7fcc986SJiri Pirko 		err = -EOPNOTSUPP;
282dd0fbc89SJiri Pirko 		goto errout;
283d7fcc986SJiri Pirko 	}
284d7fcc986SJiri Pirko 
2853c650136SJiri Pirko 	list_for_each_entry(binding, &block->binding_list, list) {
2863c650136SJiri Pirko 		err = mlxsw_sp_mall_port_rule_add(binding->mlxsw_sp_port,
2876561df56SIdo Schimmel 						  mall_entry, f->common.extack);
288d7fcc986SJiri Pirko 		if (err)
2893c650136SJiri Pirko 			goto rollback;
2903c650136SJiri Pirko 	}
291d7fcc986SJiri Pirko 
2923c650136SJiri Pirko 	block->rule_count++;
2933c650136SJiri Pirko 	if (mall_entry->ingress)
2943c650136SJiri Pirko 		block->egress_blocker_rule_count++;
2953c650136SJiri Pirko 	else
2963c650136SJiri Pirko 		block->ingress_blocker_rule_count++;
2975a2939b9SJiri Pirko 	list_add_tail(&mall_entry->list, &block->mall.list);
298aed65285SJiri Pirko 	mlxsw_sp_mall_prio_update(block);
299d7fcc986SJiri Pirko 	return 0;
300d7fcc986SJiri Pirko 
3013c650136SJiri Pirko rollback:
3023c650136SJiri Pirko 	list_for_each_entry_continue_reverse(binding, &block->binding_list,
3033c650136SJiri Pirko 					     list)
3043c650136SJiri Pirko 		mlxsw_sp_mall_port_rule_del(binding->mlxsw_sp_port, mall_entry);
305c7ea0e16SJiri Pirko errout:
306d7fcc986SJiri Pirko 	kfree(mall_entry);
307d7fcc986SJiri Pirko 	return err;
308d7fcc986SJiri Pirko }
309d7fcc986SJiri Pirko 
mlxsw_sp_mall_destroy(struct mlxsw_sp_flow_block * block,struct tc_cls_matchall_offload * f)3103c650136SJiri Pirko void mlxsw_sp_mall_destroy(struct mlxsw_sp_flow_block *block,
311d7fcc986SJiri Pirko 			   struct tc_cls_matchall_offload *f)
312d7fcc986SJiri Pirko {
3133c650136SJiri Pirko 	struct mlxsw_sp_flow_block_binding *binding;
314d7fcc986SJiri Pirko 	struct mlxsw_sp_mall_entry *mall_entry;
315d7fcc986SJiri Pirko 
3163c650136SJiri Pirko 	mall_entry = mlxsw_sp_mall_entry_find(block, f->cookie);
317d7fcc986SJiri Pirko 	if (!mall_entry) {
3183c650136SJiri Pirko 		NL_SET_ERR_MSG(f->common.extack, "Entry not found");
319d7fcc986SJiri Pirko 		return;
320d7fcc986SJiri Pirko 	}
321dd0fbc89SJiri Pirko 
322d7fcc986SJiri Pirko 	list_del(&mall_entry->list);
3233c650136SJiri Pirko 	if (mall_entry->ingress)
3243c650136SJiri Pirko 		block->egress_blocker_rule_count--;
3253c650136SJiri Pirko 	else
3263c650136SJiri Pirko 		block->ingress_blocker_rule_count--;
3273c650136SJiri Pirko 	block->rule_count--;
3283c650136SJiri Pirko 	list_for_each_entry(binding, &block->binding_list, list)
3293c650136SJiri Pirko 		mlxsw_sp_mall_port_rule_del(binding->mlxsw_sp_port, mall_entry);
330481ff57aSJiri Pirko 	kfree_rcu(mall_entry, rcu); /* sample RX packets may be in-flight */
331aed65285SJiri Pirko 	mlxsw_sp_mall_prio_update(block);
332d7fcc986SJiri Pirko }
3333c650136SJiri Pirko 
mlxsw_sp_mall_port_bind(struct mlxsw_sp_flow_block * block,struct mlxsw_sp_port * mlxsw_sp_port,struct netlink_ext_ack * extack)3343c650136SJiri Pirko int mlxsw_sp_mall_port_bind(struct mlxsw_sp_flow_block *block,
3356561df56SIdo Schimmel 			    struct mlxsw_sp_port *mlxsw_sp_port,
3366561df56SIdo Schimmel 			    struct netlink_ext_ack *extack)
3373c650136SJiri Pirko {
3383c650136SJiri Pirko 	struct mlxsw_sp_mall_entry *mall_entry;
3393c650136SJiri Pirko 	int err;
3403c650136SJiri Pirko 
3415a2939b9SJiri Pirko 	list_for_each_entry(mall_entry, &block->mall.list, list) {
3426561df56SIdo Schimmel 		err = mlxsw_sp_mall_port_rule_add(mlxsw_sp_port, mall_entry,
3436561df56SIdo Schimmel 						  extack);
3443c650136SJiri Pirko 		if (err)
3453c650136SJiri Pirko 			goto rollback;
3463c650136SJiri Pirko 	}
3473c650136SJiri Pirko 	return 0;
3483c650136SJiri Pirko 
3493c650136SJiri Pirko rollback:
3505a2939b9SJiri Pirko 	list_for_each_entry_continue_reverse(mall_entry, &block->mall.list,
3513c650136SJiri Pirko 					     list)
3523c650136SJiri Pirko 		mlxsw_sp_mall_port_rule_del(mlxsw_sp_port, mall_entry);
3533c650136SJiri Pirko 	return err;
3543c650136SJiri Pirko }
3553c650136SJiri Pirko 
mlxsw_sp_mall_port_unbind(struct mlxsw_sp_flow_block * block,struct mlxsw_sp_port * mlxsw_sp_port)3563c650136SJiri Pirko void mlxsw_sp_mall_port_unbind(struct mlxsw_sp_flow_block *block,
3573c650136SJiri Pirko 			       struct mlxsw_sp_port *mlxsw_sp_port)
3583c650136SJiri Pirko {
3593c650136SJiri Pirko 	struct mlxsw_sp_mall_entry *mall_entry;
3603c650136SJiri Pirko 
3615a2939b9SJiri Pirko 	list_for_each_entry(mall_entry, &block->mall.list, list)
3623c650136SJiri Pirko 		mlxsw_sp_mall_port_rule_del(mlxsw_sp_port, mall_entry);
3633c650136SJiri Pirko }
364aed65285SJiri Pirko 
mlxsw_sp_mall_prio_get(struct mlxsw_sp_flow_block * block,u32 chain_index,unsigned int * p_min_prio,unsigned int * p_max_prio)365aed65285SJiri Pirko int mlxsw_sp_mall_prio_get(struct mlxsw_sp_flow_block *block, u32 chain_index,
366aed65285SJiri Pirko 			   unsigned int *p_min_prio, unsigned int *p_max_prio)
367aed65285SJiri Pirko {
368aed65285SJiri Pirko 	if (chain_index || list_empty(&block->mall.list))
369aed65285SJiri Pirko 		/* In case there are no matchall rules, the caller
370aed65285SJiri Pirko 		 * receives -ENOENT to indicate there is no need
371aed65285SJiri Pirko 		 * to check the priorities.
372aed65285SJiri Pirko 		 */
373aed65285SJiri Pirko 		return -ENOENT;
374aed65285SJiri Pirko 	*p_min_prio = block->mall.min_prio;
375aed65285SJiri Pirko 	*p_max_prio = block->mall.max_prio;
376aed65285SJiri Pirko 	return 0;
377aed65285SJiri Pirko }
37820afb9bcSIdo Schimmel 
mlxsw_sp1_mall_sample_add(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_mall_entry * mall_entry,struct netlink_ext_ack * extack)37920afb9bcSIdo Schimmel static int mlxsw_sp1_mall_sample_add(struct mlxsw_sp *mlxsw_sp,
38020afb9bcSIdo Schimmel 				     struct mlxsw_sp_port *mlxsw_sp_port,
381e09a5955SIdo Schimmel 				     struct mlxsw_sp_mall_entry *mall_entry,
382559313b2SIdo Schimmel 				     struct netlink_ext_ack *extack)
38320afb9bcSIdo Schimmel {
38490f53c53SIdo Schimmel 	u32 rate = mall_entry->sample.params.rate;
385e09a5955SIdo Schimmel 
386e09a5955SIdo Schimmel 	if (!mall_entry->ingress) {
387559313b2SIdo Schimmel 		NL_SET_ERR_MSG(extack, "Sampling is not supported on egress");
388559313b2SIdo Schimmel 		return -EOPNOTSUPP;
389559313b2SIdo Schimmel 	}
390559313b2SIdo Schimmel 
391559313b2SIdo Schimmel 	if (rate > MLXSW_REG_MPSC_RATE_MAX) {
392559313b2SIdo Schimmel 		NL_SET_ERR_MSG(extack, "Unsupported sampling rate");
393559313b2SIdo Schimmel 		return -EOPNOTSUPP;
394559313b2SIdo Schimmel 	}
395559313b2SIdo Schimmel 
39620afb9bcSIdo Schimmel 	return mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, true, rate);
39720afb9bcSIdo Schimmel }
39820afb9bcSIdo Schimmel 
mlxsw_sp1_mall_sample_del(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_mall_entry * mall_entry)39920afb9bcSIdo Schimmel static void mlxsw_sp1_mall_sample_del(struct mlxsw_sp *mlxsw_sp,
400e09a5955SIdo Schimmel 				      struct mlxsw_sp_port *mlxsw_sp_port,
401e09a5955SIdo Schimmel 				      struct mlxsw_sp_mall_entry *mall_entry)
40220afb9bcSIdo Schimmel {
40320afb9bcSIdo Schimmel 	mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, false, 1);
40420afb9bcSIdo Schimmel }
40520afb9bcSIdo Schimmel 
40620afb9bcSIdo Schimmel const struct mlxsw_sp_mall_ops mlxsw_sp1_mall_ops = {
40720afb9bcSIdo Schimmel 	.sample_add = mlxsw_sp1_mall_sample_add,
40820afb9bcSIdo Schimmel 	.sample_del = mlxsw_sp1_mall_sample_del,
40920afb9bcSIdo Schimmel };
41020afb9bcSIdo Schimmel 
mlxsw_sp2_mall_sample_add(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_mall_entry * mall_entry,struct netlink_ext_ack * extack)411cf31190aSIdo Schimmel static int mlxsw_sp2_mall_sample_add(struct mlxsw_sp *mlxsw_sp,
412cf31190aSIdo Schimmel 				     struct mlxsw_sp_port *mlxsw_sp_port,
413e09a5955SIdo Schimmel 				     struct mlxsw_sp_mall_entry *mall_entry,
414559313b2SIdo Schimmel 				     struct netlink_ext_ack *extack)
415cf31190aSIdo Schimmel {
416cf31190aSIdo Schimmel 	struct mlxsw_sp_span_trigger_parms trigger_parms = {};
417cf31190aSIdo Schimmel 	struct mlxsw_sp_span_agent_parms agent_parms = {
418cf31190aSIdo Schimmel 		.to_dev = NULL,	/* Mirror to CPU. */
419cf31190aSIdo Schimmel 		.session_id = MLXSW_SP_SPAN_SESSION_ID_SAMPLING,
420cf31190aSIdo Schimmel 	};
42190f53c53SIdo Schimmel 	u32 rate = mall_entry->sample.params.rate;
42254d0e963SIdo Schimmel 	enum mlxsw_sp_span_trigger span_trigger;
423cf31190aSIdo Schimmel 	int err;
424cf31190aSIdo Schimmel 
42590f53c53SIdo Schimmel 	err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->sample.span_id,
42690f53c53SIdo Schimmel 				      &agent_parms);
4276561df56SIdo Schimmel 	if (err) {
4286561df56SIdo Schimmel 		NL_SET_ERR_MSG(extack, "Failed to get SPAN agent");
429cf31190aSIdo Schimmel 		return err;
4306561df56SIdo Schimmel 	}
431cf31190aSIdo Schimmel 
43254d0e963SIdo Schimmel 	err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port,
43354d0e963SIdo Schimmel 					      mall_entry->ingress);
4346561df56SIdo Schimmel 	if (err) {
4356561df56SIdo Schimmel 		NL_SET_ERR_MSG(extack, "Failed to get analyzed port");
436cf31190aSIdo Schimmel 		goto err_analyzed_port_get;
4376561df56SIdo Schimmel 	}
438cf31190aSIdo Schimmel 
43954d0e963SIdo Schimmel 	span_trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
44054d0e963SIdo Schimmel 					     MLXSW_SP_SPAN_TRIGGER_EGRESS;
44190f53c53SIdo Schimmel 	trigger_parms.span_id = mall_entry->sample.span_id;
442cf31190aSIdo Schimmel 	trigger_parms.probability_rate = rate;
44354d0e963SIdo Schimmel 	err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port,
44454d0e963SIdo Schimmel 				       &trigger_parms);
4456561df56SIdo Schimmel 	if (err) {
4466561df56SIdo Schimmel 		NL_SET_ERR_MSG(extack, "Failed to bind SPAN agent");
447cf31190aSIdo Schimmel 		goto err_agent_bind;
4486561df56SIdo Schimmel 	}
449cf31190aSIdo Schimmel 
450cf31190aSIdo Schimmel 	return 0;
451cf31190aSIdo Schimmel 
452cf31190aSIdo Schimmel err_agent_bind:
45354d0e963SIdo Schimmel 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
454cf31190aSIdo Schimmel err_analyzed_port_get:
45590f53c53SIdo Schimmel 	mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->sample.span_id);
456cf31190aSIdo Schimmel 	return err;
457cf31190aSIdo Schimmel }
458cf31190aSIdo Schimmel 
mlxsw_sp2_mall_sample_del(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_mall_entry * mall_entry)459cf31190aSIdo Schimmel static void mlxsw_sp2_mall_sample_del(struct mlxsw_sp *mlxsw_sp,
460e09a5955SIdo Schimmel 				      struct mlxsw_sp_port *mlxsw_sp_port,
461e09a5955SIdo Schimmel 				      struct mlxsw_sp_mall_entry *mall_entry)
462cf31190aSIdo Schimmel {
463cf31190aSIdo Schimmel 	struct mlxsw_sp_span_trigger_parms trigger_parms = {};
46454d0e963SIdo Schimmel 	enum mlxsw_sp_span_trigger span_trigger;
465cf31190aSIdo Schimmel 
46654d0e963SIdo Schimmel 	span_trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
46754d0e963SIdo Schimmel 					     MLXSW_SP_SPAN_TRIGGER_EGRESS;
46890f53c53SIdo Schimmel 	trigger_parms.span_id = mall_entry->sample.span_id;
46954d0e963SIdo Schimmel 	mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,
47054d0e963SIdo Schimmel 				   &trigger_parms);
47154d0e963SIdo Schimmel 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
47290f53c53SIdo Schimmel 	mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->sample.span_id);
473cf31190aSIdo Schimmel }
474cf31190aSIdo Schimmel 
47520afb9bcSIdo Schimmel const struct mlxsw_sp_mall_ops mlxsw_sp2_mall_ops = {
476cf31190aSIdo Schimmel 	.sample_add = mlxsw_sp2_mall_sample_add,
477cf31190aSIdo Schimmel 	.sample_del = mlxsw_sp2_mall_sample_del,
47820afb9bcSIdo Schimmel };
479