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