162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2017-2020 Mellanox Technologies. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/errno.h>
662306a36Sopenharmony_ci#include <linux/netdevice.h>
762306a36Sopenharmony_ci#include <net/flow_offload.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "spectrum.h"
1062306a36Sopenharmony_ci#include "spectrum_span.h"
1162306a36Sopenharmony_ci#include "reg.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic struct mlxsw_sp_mall_entry *
1462306a36Sopenharmony_cimlxsw_sp_mall_entry_find(struct mlxsw_sp_flow_block *block, unsigned long cookie)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	struct mlxsw_sp_mall_entry *mall_entry;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	list_for_each_entry(mall_entry, &block->mall.list, list)
1962306a36Sopenharmony_ci		if (mall_entry->cookie == cookie)
2062306a36Sopenharmony_ci			return mall_entry;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	return NULL;
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int
2662306a36Sopenharmony_cimlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port,
2762306a36Sopenharmony_ci			      struct mlxsw_sp_mall_entry *mall_entry,
2862306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3162306a36Sopenharmony_ci	struct mlxsw_sp_span_agent_parms agent_parms = {};
3262306a36Sopenharmony_ci	struct mlxsw_sp_span_trigger_parms parms;
3362306a36Sopenharmony_ci	enum mlxsw_sp_span_trigger trigger;
3462306a36Sopenharmony_ci	int err;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (!mall_entry->mirror.to_dev) {
3762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Could not find requested device");
3862306a36Sopenharmony_ci		return -EINVAL;
3962306a36Sopenharmony_ci	}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	agent_parms.to_dev = mall_entry->mirror.to_dev;
4262306a36Sopenharmony_ci	err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->mirror.span_id,
4362306a36Sopenharmony_ci				      &agent_parms);
4462306a36Sopenharmony_ci	if (err) {
4562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Failed to get SPAN agent");
4662306a36Sopenharmony_ci		return err;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port,
5062306a36Sopenharmony_ci					      mall_entry->ingress);
5162306a36Sopenharmony_ci	if (err) {
5262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Failed to get analyzed port");
5362306a36Sopenharmony_ci		goto err_analyzed_port_get;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
5762306a36Sopenharmony_ci					MLXSW_SP_SPAN_TRIGGER_EGRESS;
5862306a36Sopenharmony_ci	parms.span_id = mall_entry->mirror.span_id;
5962306a36Sopenharmony_ci	parms.probability_rate = 1;
6062306a36Sopenharmony_ci	err = mlxsw_sp_span_agent_bind(mlxsw_sp, trigger, mlxsw_sp_port,
6162306a36Sopenharmony_ci				       &parms);
6262306a36Sopenharmony_ci	if (err) {
6362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Failed to bind SPAN agent");
6462306a36Sopenharmony_ci		goto err_agent_bind;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cierr_agent_bind:
7062306a36Sopenharmony_ci	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
7162306a36Sopenharmony_cierr_analyzed_port_get:
7262306a36Sopenharmony_ci	mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->mirror.span_id);
7362306a36Sopenharmony_ci	return err;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void
7762306a36Sopenharmony_cimlxsw_sp_mall_port_mirror_del(struct mlxsw_sp_port *mlxsw_sp_port,
7862306a36Sopenharmony_ci			      struct mlxsw_sp_mall_entry *mall_entry)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
8162306a36Sopenharmony_ci	struct mlxsw_sp_span_trigger_parms parms;
8262306a36Sopenharmony_ci	enum mlxsw_sp_span_trigger trigger;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
8562306a36Sopenharmony_ci					MLXSW_SP_SPAN_TRIGGER_EGRESS;
8662306a36Sopenharmony_ci	parms.span_id = mall_entry->mirror.span_id;
8762306a36Sopenharmony_ci	mlxsw_sp_span_agent_unbind(mlxsw_sp, trigger, mlxsw_sp_port, &parms);
8862306a36Sopenharmony_ci	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
8962306a36Sopenharmony_ci	mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->mirror.span_id);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int mlxsw_sp_mall_port_sample_set(struct mlxsw_sp_port *mlxsw_sp_port,
9362306a36Sopenharmony_ci					 bool enable, u32 rate)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
9662306a36Sopenharmony_ci	char mpsc_pl[MLXSW_REG_MPSC_LEN];
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	mlxsw_reg_mpsc_pack(mpsc_pl, mlxsw_sp_port->local_port, enable, rate);
9962306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpsc), mpsc_pl);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int
10362306a36Sopenharmony_cimlxsw_sp_mall_port_sample_add(struct mlxsw_sp_port *mlxsw_sp_port,
10462306a36Sopenharmony_ci			      struct mlxsw_sp_mall_entry *mall_entry,
10562306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
10862306a36Sopenharmony_ci	struct mlxsw_sp_sample_trigger trigger;
10962306a36Sopenharmony_ci	int err;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (mall_entry->ingress)
11262306a36Sopenharmony_ci		trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS;
11362306a36Sopenharmony_ci	else
11462306a36Sopenharmony_ci		trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS;
11562306a36Sopenharmony_ci	trigger.local_port = mlxsw_sp_port->local_port;
11662306a36Sopenharmony_ci	err = mlxsw_sp_sample_trigger_params_set(mlxsw_sp, &trigger,
11762306a36Sopenharmony_ci						 &mall_entry->sample.params,
11862306a36Sopenharmony_ci						 extack);
11962306a36Sopenharmony_ci	if (err)
12062306a36Sopenharmony_ci		return err;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	err = mlxsw_sp->mall_ops->sample_add(mlxsw_sp, mlxsw_sp_port,
12362306a36Sopenharmony_ci					     mall_entry, extack);
12462306a36Sopenharmony_ci	if (err)
12562306a36Sopenharmony_ci		goto err_port_sample_set;
12662306a36Sopenharmony_ci	return 0;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cierr_port_sample_set:
12962306a36Sopenharmony_ci	mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
13062306a36Sopenharmony_ci	return err;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void
13462306a36Sopenharmony_cimlxsw_sp_mall_port_sample_del(struct mlxsw_sp_port *mlxsw_sp_port,
13562306a36Sopenharmony_ci			      struct mlxsw_sp_mall_entry *mall_entry)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
13862306a36Sopenharmony_ci	struct mlxsw_sp_sample_trigger trigger;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (mall_entry->ingress)
14162306a36Sopenharmony_ci		trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS;
14262306a36Sopenharmony_ci	else
14362306a36Sopenharmony_ci		trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS;
14462306a36Sopenharmony_ci	trigger.local_port = mlxsw_sp_port->local_port;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	mlxsw_sp->mall_ops->sample_del(mlxsw_sp, mlxsw_sp_port, mall_entry);
14762306a36Sopenharmony_ci	mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int
15162306a36Sopenharmony_cimlxsw_sp_mall_port_rule_add(struct mlxsw_sp_port *mlxsw_sp_port,
15262306a36Sopenharmony_ci			    struct mlxsw_sp_mall_entry *mall_entry,
15362306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	switch (mall_entry->type) {
15662306a36Sopenharmony_ci	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
15762306a36Sopenharmony_ci		return mlxsw_sp_mall_port_mirror_add(mlxsw_sp_port, mall_entry,
15862306a36Sopenharmony_ci						     extack);
15962306a36Sopenharmony_ci	case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE:
16062306a36Sopenharmony_ci		return mlxsw_sp_mall_port_sample_add(mlxsw_sp_port, mall_entry,
16162306a36Sopenharmony_ci						     extack);
16262306a36Sopenharmony_ci	default:
16362306a36Sopenharmony_ci		WARN_ON(1);
16462306a36Sopenharmony_ci		return -EINVAL;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void
16962306a36Sopenharmony_cimlxsw_sp_mall_port_rule_del(struct mlxsw_sp_port *mlxsw_sp_port,
17062306a36Sopenharmony_ci			    struct mlxsw_sp_mall_entry *mall_entry)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	switch (mall_entry->type) {
17362306a36Sopenharmony_ci	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
17462306a36Sopenharmony_ci		mlxsw_sp_mall_port_mirror_del(mlxsw_sp_port, mall_entry);
17562306a36Sopenharmony_ci		break;
17662306a36Sopenharmony_ci	case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE:
17762306a36Sopenharmony_ci		mlxsw_sp_mall_port_sample_del(mlxsw_sp_port, mall_entry);
17862306a36Sopenharmony_ci		break;
17962306a36Sopenharmony_ci	default:
18062306a36Sopenharmony_ci		WARN_ON(1);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic void mlxsw_sp_mall_prio_update(struct mlxsw_sp_flow_block *block)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct mlxsw_sp_mall_entry *mall_entry;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (list_empty(&block->mall.list))
18962306a36Sopenharmony_ci		return;
19062306a36Sopenharmony_ci	block->mall.min_prio = UINT_MAX;
19162306a36Sopenharmony_ci	block->mall.max_prio = 0;
19262306a36Sopenharmony_ci	list_for_each_entry(mall_entry, &block->mall.list, list) {
19362306a36Sopenharmony_ci		if (mall_entry->priority < block->mall.min_prio)
19462306a36Sopenharmony_ci			block->mall.min_prio = mall_entry->priority;
19562306a36Sopenharmony_ci		if (mall_entry->priority > block->mall.max_prio)
19662306a36Sopenharmony_ci			block->mall.max_prio = mall_entry->priority;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ciint mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp,
20162306a36Sopenharmony_ci			  struct mlxsw_sp_flow_block *block,
20262306a36Sopenharmony_ci			  struct tc_cls_matchall_offload *f)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct mlxsw_sp_flow_block_binding *binding;
20562306a36Sopenharmony_ci	struct mlxsw_sp_mall_entry *mall_entry;
20662306a36Sopenharmony_ci	__be16 protocol = f->common.protocol;
20762306a36Sopenharmony_ci	struct flow_action_entry *act;
20862306a36Sopenharmony_ci	unsigned int flower_min_prio;
20962306a36Sopenharmony_ci	unsigned int flower_max_prio;
21062306a36Sopenharmony_ci	bool flower_prio_valid;
21162306a36Sopenharmony_ci	int err;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (!flow_offload_has_one_action(&f->rule->action)) {
21462306a36Sopenharmony_ci		NL_SET_ERR_MSG(f->common.extack, "Only singular actions are supported");
21562306a36Sopenharmony_ci		return -EOPNOTSUPP;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (f->common.chain_index) {
21962306a36Sopenharmony_ci		NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported");
22062306a36Sopenharmony_ci		return -EOPNOTSUPP;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (mlxsw_sp_flow_block_is_mixed_bound(block)) {
22462306a36Sopenharmony_ci		NL_SET_ERR_MSG(f->common.extack, "Only not mixed bound blocks are supported");
22562306a36Sopenharmony_ci		return -EOPNOTSUPP;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	err = mlxsw_sp_flower_prio_get(mlxsw_sp, block, f->common.chain_index,
22962306a36Sopenharmony_ci				       &flower_min_prio, &flower_max_prio);
23062306a36Sopenharmony_ci	if (err) {
23162306a36Sopenharmony_ci		if (err != -ENOENT) {
23262306a36Sopenharmony_ci			NL_SET_ERR_MSG(f->common.extack, "Failed to get flower priorities");
23362306a36Sopenharmony_ci			return err;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci		flower_prio_valid = false;
23662306a36Sopenharmony_ci		/* No flower filters are installed in specified chain. */
23762306a36Sopenharmony_ci	} else {
23862306a36Sopenharmony_ci		flower_prio_valid = true;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (protocol != htons(ETH_P_ALL)) {
24262306a36Sopenharmony_ci		NL_SET_ERR_MSG(f->common.extack, "matchall rules only supported with 'all' protocol");
24362306a36Sopenharmony_ci		return -EOPNOTSUPP;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
24762306a36Sopenharmony_ci	if (!mall_entry)
24862306a36Sopenharmony_ci		return -ENOMEM;
24962306a36Sopenharmony_ci	mall_entry->cookie = f->cookie;
25062306a36Sopenharmony_ci	mall_entry->priority = f->common.prio;
25162306a36Sopenharmony_ci	mall_entry->ingress = mlxsw_sp_flow_block_is_ingress_bound(block);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (flower_prio_valid && mall_entry->ingress &&
25462306a36Sopenharmony_ci	    mall_entry->priority >= flower_min_prio) {
25562306a36Sopenharmony_ci		NL_SET_ERR_MSG(f->common.extack, "Failed to add behind existing flower rules");
25662306a36Sopenharmony_ci		err = -EOPNOTSUPP;
25762306a36Sopenharmony_ci		goto errout;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci	if (flower_prio_valid && !mall_entry->ingress &&
26062306a36Sopenharmony_ci	    mall_entry->priority <= flower_max_prio) {
26162306a36Sopenharmony_ci		NL_SET_ERR_MSG(f->common.extack, "Failed to add in front of existing flower rules");
26262306a36Sopenharmony_ci		err = -EOPNOTSUPP;
26362306a36Sopenharmony_ci		goto errout;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	act = &f->rule->action.entries[0];
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	switch (act->id) {
26962306a36Sopenharmony_ci	case FLOW_ACTION_MIRRED:
27062306a36Sopenharmony_ci		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR;
27162306a36Sopenharmony_ci		mall_entry->mirror.to_dev = act->dev;
27262306a36Sopenharmony_ci		break;
27362306a36Sopenharmony_ci	case FLOW_ACTION_SAMPLE:
27462306a36Sopenharmony_ci		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_SAMPLE;
27562306a36Sopenharmony_ci		mall_entry->sample.params.psample_group = act->sample.psample_group;
27662306a36Sopenharmony_ci		mall_entry->sample.params.truncate = act->sample.truncate;
27762306a36Sopenharmony_ci		mall_entry->sample.params.trunc_size = act->sample.trunc_size;
27862306a36Sopenharmony_ci		mall_entry->sample.params.rate = act->sample.rate;
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	default:
28162306a36Sopenharmony_ci		err = -EOPNOTSUPP;
28262306a36Sopenharmony_ci		goto errout;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	list_for_each_entry(binding, &block->binding_list, list) {
28662306a36Sopenharmony_ci		err = mlxsw_sp_mall_port_rule_add(binding->mlxsw_sp_port,
28762306a36Sopenharmony_ci						  mall_entry, f->common.extack);
28862306a36Sopenharmony_ci		if (err)
28962306a36Sopenharmony_ci			goto rollback;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	block->rule_count++;
29362306a36Sopenharmony_ci	if (mall_entry->ingress)
29462306a36Sopenharmony_ci		block->egress_blocker_rule_count++;
29562306a36Sopenharmony_ci	else
29662306a36Sopenharmony_ci		block->ingress_blocker_rule_count++;
29762306a36Sopenharmony_ci	list_add_tail(&mall_entry->list, &block->mall.list);
29862306a36Sopenharmony_ci	mlxsw_sp_mall_prio_update(block);
29962306a36Sopenharmony_ci	return 0;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cirollback:
30262306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(binding, &block->binding_list,
30362306a36Sopenharmony_ci					     list)
30462306a36Sopenharmony_ci		mlxsw_sp_mall_port_rule_del(binding->mlxsw_sp_port, mall_entry);
30562306a36Sopenharmony_cierrout:
30662306a36Sopenharmony_ci	kfree(mall_entry);
30762306a36Sopenharmony_ci	return err;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_civoid mlxsw_sp_mall_destroy(struct mlxsw_sp_flow_block *block,
31162306a36Sopenharmony_ci			   struct tc_cls_matchall_offload *f)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct mlxsw_sp_flow_block_binding *binding;
31462306a36Sopenharmony_ci	struct mlxsw_sp_mall_entry *mall_entry;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	mall_entry = mlxsw_sp_mall_entry_find(block, f->cookie);
31762306a36Sopenharmony_ci	if (!mall_entry) {
31862306a36Sopenharmony_ci		NL_SET_ERR_MSG(f->common.extack, "Entry not found");
31962306a36Sopenharmony_ci		return;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	list_del(&mall_entry->list);
32362306a36Sopenharmony_ci	if (mall_entry->ingress)
32462306a36Sopenharmony_ci		block->egress_blocker_rule_count--;
32562306a36Sopenharmony_ci	else
32662306a36Sopenharmony_ci		block->ingress_blocker_rule_count--;
32762306a36Sopenharmony_ci	block->rule_count--;
32862306a36Sopenharmony_ci	list_for_each_entry(binding, &block->binding_list, list)
32962306a36Sopenharmony_ci		mlxsw_sp_mall_port_rule_del(binding->mlxsw_sp_port, mall_entry);
33062306a36Sopenharmony_ci	kfree_rcu(mall_entry, rcu); /* sample RX packets may be in-flight */
33162306a36Sopenharmony_ci	mlxsw_sp_mall_prio_update(block);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ciint mlxsw_sp_mall_port_bind(struct mlxsw_sp_flow_block *block,
33562306a36Sopenharmony_ci			    struct mlxsw_sp_port *mlxsw_sp_port,
33662306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct mlxsw_sp_mall_entry *mall_entry;
33962306a36Sopenharmony_ci	int err;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	list_for_each_entry(mall_entry, &block->mall.list, list) {
34262306a36Sopenharmony_ci		err = mlxsw_sp_mall_port_rule_add(mlxsw_sp_port, mall_entry,
34362306a36Sopenharmony_ci						  extack);
34462306a36Sopenharmony_ci		if (err)
34562306a36Sopenharmony_ci			goto rollback;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci	return 0;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cirollback:
35062306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(mall_entry, &block->mall.list,
35162306a36Sopenharmony_ci					     list)
35262306a36Sopenharmony_ci		mlxsw_sp_mall_port_rule_del(mlxsw_sp_port, mall_entry);
35362306a36Sopenharmony_ci	return err;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_civoid mlxsw_sp_mall_port_unbind(struct mlxsw_sp_flow_block *block,
35762306a36Sopenharmony_ci			       struct mlxsw_sp_port *mlxsw_sp_port)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct mlxsw_sp_mall_entry *mall_entry;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	list_for_each_entry(mall_entry, &block->mall.list, list)
36262306a36Sopenharmony_ci		mlxsw_sp_mall_port_rule_del(mlxsw_sp_port, mall_entry);
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ciint mlxsw_sp_mall_prio_get(struct mlxsw_sp_flow_block *block, u32 chain_index,
36662306a36Sopenharmony_ci			   unsigned int *p_min_prio, unsigned int *p_max_prio)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	if (chain_index || list_empty(&block->mall.list))
36962306a36Sopenharmony_ci		/* In case there are no matchall rules, the caller
37062306a36Sopenharmony_ci		 * receives -ENOENT to indicate there is no need
37162306a36Sopenharmony_ci		 * to check the priorities.
37262306a36Sopenharmony_ci		 */
37362306a36Sopenharmony_ci		return -ENOENT;
37462306a36Sopenharmony_ci	*p_min_prio = block->mall.min_prio;
37562306a36Sopenharmony_ci	*p_max_prio = block->mall.max_prio;
37662306a36Sopenharmony_ci	return 0;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int mlxsw_sp1_mall_sample_add(struct mlxsw_sp *mlxsw_sp,
38062306a36Sopenharmony_ci				     struct mlxsw_sp_port *mlxsw_sp_port,
38162306a36Sopenharmony_ci				     struct mlxsw_sp_mall_entry *mall_entry,
38262306a36Sopenharmony_ci				     struct netlink_ext_ack *extack)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	u32 rate = mall_entry->sample.params.rate;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (!mall_entry->ingress) {
38762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Sampling is not supported on egress");
38862306a36Sopenharmony_ci		return -EOPNOTSUPP;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (rate > MLXSW_REG_MPSC_RATE_MAX) {
39262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Unsupported sampling rate");
39362306a36Sopenharmony_ci		return -EOPNOTSUPP;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, true, rate);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void mlxsw_sp1_mall_sample_del(struct mlxsw_sp *mlxsw_sp,
40062306a36Sopenharmony_ci				      struct mlxsw_sp_port *mlxsw_sp_port,
40162306a36Sopenharmony_ci				      struct mlxsw_sp_mall_entry *mall_entry)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, false, 1);
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ciconst struct mlxsw_sp_mall_ops mlxsw_sp1_mall_ops = {
40762306a36Sopenharmony_ci	.sample_add = mlxsw_sp1_mall_sample_add,
40862306a36Sopenharmony_ci	.sample_del = mlxsw_sp1_mall_sample_del,
40962306a36Sopenharmony_ci};
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic int mlxsw_sp2_mall_sample_add(struct mlxsw_sp *mlxsw_sp,
41262306a36Sopenharmony_ci				     struct mlxsw_sp_port *mlxsw_sp_port,
41362306a36Sopenharmony_ci				     struct mlxsw_sp_mall_entry *mall_entry,
41462306a36Sopenharmony_ci				     struct netlink_ext_ack *extack)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct mlxsw_sp_span_trigger_parms trigger_parms = {};
41762306a36Sopenharmony_ci	struct mlxsw_sp_span_agent_parms agent_parms = {
41862306a36Sopenharmony_ci		.to_dev = NULL,	/* Mirror to CPU. */
41962306a36Sopenharmony_ci		.session_id = MLXSW_SP_SPAN_SESSION_ID_SAMPLING,
42062306a36Sopenharmony_ci	};
42162306a36Sopenharmony_ci	u32 rate = mall_entry->sample.params.rate;
42262306a36Sopenharmony_ci	enum mlxsw_sp_span_trigger span_trigger;
42362306a36Sopenharmony_ci	int err;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->sample.span_id,
42662306a36Sopenharmony_ci				      &agent_parms);
42762306a36Sopenharmony_ci	if (err) {
42862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Failed to get SPAN agent");
42962306a36Sopenharmony_ci		return err;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port,
43362306a36Sopenharmony_ci					      mall_entry->ingress);
43462306a36Sopenharmony_ci	if (err) {
43562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Failed to get analyzed port");
43662306a36Sopenharmony_ci		goto err_analyzed_port_get;
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	span_trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
44062306a36Sopenharmony_ci					     MLXSW_SP_SPAN_TRIGGER_EGRESS;
44162306a36Sopenharmony_ci	trigger_parms.span_id = mall_entry->sample.span_id;
44262306a36Sopenharmony_ci	trigger_parms.probability_rate = rate;
44362306a36Sopenharmony_ci	err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port,
44462306a36Sopenharmony_ci				       &trigger_parms);
44562306a36Sopenharmony_ci	if (err) {
44662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Failed to bind SPAN agent");
44762306a36Sopenharmony_ci		goto err_agent_bind;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	return 0;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cierr_agent_bind:
45362306a36Sopenharmony_ci	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
45462306a36Sopenharmony_cierr_analyzed_port_get:
45562306a36Sopenharmony_ci	mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->sample.span_id);
45662306a36Sopenharmony_ci	return err;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic void mlxsw_sp2_mall_sample_del(struct mlxsw_sp *mlxsw_sp,
46062306a36Sopenharmony_ci				      struct mlxsw_sp_port *mlxsw_sp_port,
46162306a36Sopenharmony_ci				      struct mlxsw_sp_mall_entry *mall_entry)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct mlxsw_sp_span_trigger_parms trigger_parms = {};
46462306a36Sopenharmony_ci	enum mlxsw_sp_span_trigger span_trigger;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	span_trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
46762306a36Sopenharmony_ci					     MLXSW_SP_SPAN_TRIGGER_EGRESS;
46862306a36Sopenharmony_ci	trigger_parms.span_id = mall_entry->sample.span_id;
46962306a36Sopenharmony_ci	mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,
47062306a36Sopenharmony_ci				   &trigger_parms);
47162306a36Sopenharmony_ci	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
47262306a36Sopenharmony_ci	mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->sample.span_id);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ciconst struct mlxsw_sp_mall_ops mlxsw_sp2_mall_ops = {
47662306a36Sopenharmony_ci	.sample_add = mlxsw_sp2_mall_sample_add,
47762306a36Sopenharmony_ci	.sample_del = mlxsw_sp2_mall_sample_del,
47862306a36Sopenharmony_ci};
479