spectrum_matchall.c (14214B)
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/errno.h> 6#include <linux/netdevice.h> 7#include <net/flow_offload.h> 8 9#include "spectrum.h" 10#include "spectrum_span.h" 11#include "reg.h" 12 13static struct mlxsw_sp_mall_entry * 14mlxsw_sp_mall_entry_find(struct mlxsw_sp_flow_block *block, unsigned long cookie) 15{ 16 struct mlxsw_sp_mall_entry *mall_entry; 17 18 list_for_each_entry(mall_entry, &block->mall.list, list) 19 if (mall_entry->cookie == cookie) 20 return mall_entry; 21 22 return NULL; 23} 24 25static int 26mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port, 27 struct mlxsw_sp_mall_entry *mall_entry, 28 struct netlink_ext_ack *extack) 29{ 30 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 31 struct mlxsw_sp_span_agent_parms agent_parms = {}; 32 struct mlxsw_sp_span_trigger_parms parms; 33 enum mlxsw_sp_span_trigger trigger; 34 int err; 35 36 if (!mall_entry->mirror.to_dev) { 37 NL_SET_ERR_MSG(extack, "Could not find requested device"); 38 return -EINVAL; 39 } 40 41 agent_parms.to_dev = mall_entry->mirror.to_dev; 42 err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->mirror.span_id, 43 &agent_parms); 44 if (err) { 45 NL_SET_ERR_MSG(extack, "Failed to get SPAN agent"); 46 return err; 47 } 48 49 err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, 50 mall_entry->ingress); 51 if (err) { 52 NL_SET_ERR_MSG(extack, "Failed to get analyzed port"); 53 goto err_analyzed_port_get; 54 } 55 56 trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS : 57 MLXSW_SP_SPAN_TRIGGER_EGRESS; 58 parms.span_id = mall_entry->mirror.span_id; 59 parms.probability_rate = 1; 60 err = mlxsw_sp_span_agent_bind(mlxsw_sp, trigger, mlxsw_sp_port, 61 &parms); 62 if (err) { 63 NL_SET_ERR_MSG(extack, "Failed to bind SPAN agent"); 64 goto err_agent_bind; 65 } 66 67 return 0; 68 69err_agent_bind: 70 mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress); 71err_analyzed_port_get: 72 mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->mirror.span_id); 73 return err; 74} 75 76static void 77mlxsw_sp_mall_port_mirror_del(struct mlxsw_sp_port *mlxsw_sp_port, 78 struct mlxsw_sp_mall_entry *mall_entry) 79{ 80 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 81 struct mlxsw_sp_span_trigger_parms parms; 82 enum mlxsw_sp_span_trigger trigger; 83 84 trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS : 85 MLXSW_SP_SPAN_TRIGGER_EGRESS; 86 parms.span_id = mall_entry->mirror.span_id; 87 mlxsw_sp_span_agent_unbind(mlxsw_sp, trigger, mlxsw_sp_port, &parms); 88 mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress); 89 mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->mirror.span_id); 90} 91 92static int mlxsw_sp_mall_port_sample_set(struct mlxsw_sp_port *mlxsw_sp_port, 93 bool enable, u32 rate) 94{ 95 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 96 char mpsc_pl[MLXSW_REG_MPSC_LEN]; 97 98 mlxsw_reg_mpsc_pack(mpsc_pl, mlxsw_sp_port->local_port, enable, rate); 99 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpsc), mpsc_pl); 100} 101 102static int 103mlxsw_sp_mall_port_sample_add(struct mlxsw_sp_port *mlxsw_sp_port, 104 struct mlxsw_sp_mall_entry *mall_entry, 105 struct netlink_ext_ack *extack) 106{ 107 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 108 struct mlxsw_sp_sample_trigger trigger; 109 int err; 110 111 if (mall_entry->ingress) 112 trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS; 113 else 114 trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS; 115 trigger.local_port = mlxsw_sp_port->local_port; 116 err = mlxsw_sp_sample_trigger_params_set(mlxsw_sp, &trigger, 117 &mall_entry->sample.params, 118 extack); 119 if (err) 120 return err; 121 122 err = mlxsw_sp->mall_ops->sample_add(mlxsw_sp, mlxsw_sp_port, 123 mall_entry, extack); 124 if (err) 125 goto err_port_sample_set; 126 return 0; 127 128err_port_sample_set: 129 mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger); 130 return err; 131} 132 133static void 134mlxsw_sp_mall_port_sample_del(struct mlxsw_sp_port *mlxsw_sp_port, 135 struct mlxsw_sp_mall_entry *mall_entry) 136{ 137 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 138 struct mlxsw_sp_sample_trigger trigger; 139 140 if (mall_entry->ingress) 141 trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS; 142 else 143 trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS; 144 trigger.local_port = mlxsw_sp_port->local_port; 145 146 mlxsw_sp->mall_ops->sample_del(mlxsw_sp, mlxsw_sp_port, mall_entry); 147 mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger); 148} 149 150static int 151mlxsw_sp_mall_port_rule_add(struct mlxsw_sp_port *mlxsw_sp_port, 152 struct mlxsw_sp_mall_entry *mall_entry, 153 struct netlink_ext_ack *extack) 154{ 155 switch (mall_entry->type) { 156 case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 157 return mlxsw_sp_mall_port_mirror_add(mlxsw_sp_port, mall_entry, 158 extack); 159 case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE: 160 return mlxsw_sp_mall_port_sample_add(mlxsw_sp_port, mall_entry, 161 extack); 162 default: 163 WARN_ON(1); 164 return -EINVAL; 165 } 166} 167 168static void 169mlxsw_sp_mall_port_rule_del(struct mlxsw_sp_port *mlxsw_sp_port, 170 struct mlxsw_sp_mall_entry *mall_entry) 171{ 172 switch (mall_entry->type) { 173 case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 174 mlxsw_sp_mall_port_mirror_del(mlxsw_sp_port, mall_entry); 175 break; 176 case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE: 177 mlxsw_sp_mall_port_sample_del(mlxsw_sp_port, mall_entry); 178 break; 179 default: 180 WARN_ON(1); 181 } 182} 183 184static void mlxsw_sp_mall_prio_update(struct mlxsw_sp_flow_block *block) 185{ 186 struct mlxsw_sp_mall_entry *mall_entry; 187 188 if (list_empty(&block->mall.list)) 189 return; 190 block->mall.min_prio = UINT_MAX; 191 block->mall.max_prio = 0; 192 list_for_each_entry(mall_entry, &block->mall.list, list) { 193 if (mall_entry->priority < block->mall.min_prio) 194 block->mall.min_prio = mall_entry->priority; 195 if (mall_entry->priority > block->mall.max_prio) 196 block->mall.max_prio = mall_entry->priority; 197 } 198} 199 200int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp, 201 struct mlxsw_sp_flow_block *block, 202 struct tc_cls_matchall_offload *f) 203{ 204 struct mlxsw_sp_flow_block_binding *binding; 205 struct mlxsw_sp_mall_entry *mall_entry; 206 __be16 protocol = f->common.protocol; 207 struct flow_action_entry *act; 208 unsigned int flower_min_prio; 209 unsigned int flower_max_prio; 210 bool flower_prio_valid; 211 int err; 212 213 if (!flow_offload_has_one_action(&f->rule->action)) { 214 NL_SET_ERR_MSG(f->common.extack, "Only singular actions are supported"); 215 return -EOPNOTSUPP; 216 } 217 218 if (f->common.chain_index) { 219 NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported"); 220 return -EOPNOTSUPP; 221 } 222 223 if (mlxsw_sp_flow_block_is_mixed_bound(block)) { 224 NL_SET_ERR_MSG(f->common.extack, "Only not mixed bound blocks are supported"); 225 return -EOPNOTSUPP; 226 } 227 228 err = mlxsw_sp_flower_prio_get(mlxsw_sp, block, f->common.chain_index, 229 &flower_min_prio, &flower_max_prio); 230 if (err) { 231 if (err != -ENOENT) { 232 NL_SET_ERR_MSG(f->common.extack, "Failed to get flower priorities"); 233 return err; 234 } 235 flower_prio_valid = false; 236 /* No flower filters are installed in specified chain. */ 237 } else { 238 flower_prio_valid = true; 239 } 240 241 if (protocol != htons(ETH_P_ALL)) { 242 NL_SET_ERR_MSG(f->common.extack, "matchall rules only supported with 'all' protocol"); 243 return -EOPNOTSUPP; 244 } 245 246 mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL); 247 if (!mall_entry) 248 return -ENOMEM; 249 mall_entry->cookie = f->cookie; 250 mall_entry->priority = f->common.prio; 251 mall_entry->ingress = mlxsw_sp_flow_block_is_ingress_bound(block); 252 253 if (flower_prio_valid && mall_entry->ingress && 254 mall_entry->priority >= flower_min_prio) { 255 NL_SET_ERR_MSG(f->common.extack, "Failed to add behind existing flower rules"); 256 err = -EOPNOTSUPP; 257 goto errout; 258 } 259 if (flower_prio_valid && !mall_entry->ingress && 260 mall_entry->priority <= flower_max_prio) { 261 NL_SET_ERR_MSG(f->common.extack, "Failed to add in front of existing flower rules"); 262 err = -EOPNOTSUPP; 263 goto errout; 264 } 265 266 act = &f->rule->action.entries[0]; 267 268 switch (act->id) { 269 case FLOW_ACTION_MIRRED: 270 mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR; 271 mall_entry->mirror.to_dev = act->dev; 272 break; 273 case FLOW_ACTION_SAMPLE: 274 mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_SAMPLE; 275 mall_entry->sample.params.psample_group = act->sample.psample_group; 276 mall_entry->sample.params.truncate = act->sample.truncate; 277 mall_entry->sample.params.trunc_size = act->sample.trunc_size; 278 mall_entry->sample.params.rate = act->sample.rate; 279 break; 280 default: 281 err = -EOPNOTSUPP; 282 goto errout; 283 } 284 285 list_for_each_entry(binding, &block->binding_list, list) { 286 err = mlxsw_sp_mall_port_rule_add(binding->mlxsw_sp_port, 287 mall_entry, f->common.extack); 288 if (err) 289 goto rollback; 290 } 291 292 block->rule_count++; 293 if (mall_entry->ingress) 294 block->egress_blocker_rule_count++; 295 else 296 block->ingress_blocker_rule_count++; 297 list_add_tail(&mall_entry->list, &block->mall.list); 298 mlxsw_sp_mall_prio_update(block); 299 return 0; 300 301rollback: 302 list_for_each_entry_continue_reverse(binding, &block->binding_list, 303 list) 304 mlxsw_sp_mall_port_rule_del(binding->mlxsw_sp_port, mall_entry); 305errout: 306 kfree(mall_entry); 307 return err; 308} 309 310void mlxsw_sp_mall_destroy(struct mlxsw_sp_flow_block *block, 311 struct tc_cls_matchall_offload *f) 312{ 313 struct mlxsw_sp_flow_block_binding *binding; 314 struct mlxsw_sp_mall_entry *mall_entry; 315 316 mall_entry = mlxsw_sp_mall_entry_find(block, f->cookie); 317 if (!mall_entry) { 318 NL_SET_ERR_MSG(f->common.extack, "Entry not found"); 319 return; 320 } 321 322 list_del(&mall_entry->list); 323 if (mall_entry->ingress) 324 block->egress_blocker_rule_count--; 325 else 326 block->ingress_blocker_rule_count--; 327 block->rule_count--; 328 list_for_each_entry(binding, &block->binding_list, list) 329 mlxsw_sp_mall_port_rule_del(binding->mlxsw_sp_port, mall_entry); 330 kfree_rcu(mall_entry, rcu); /* sample RX packets may be in-flight */ 331 mlxsw_sp_mall_prio_update(block); 332} 333 334int mlxsw_sp_mall_port_bind(struct mlxsw_sp_flow_block *block, 335 struct mlxsw_sp_port *mlxsw_sp_port, 336 struct netlink_ext_ack *extack) 337{ 338 struct mlxsw_sp_mall_entry *mall_entry; 339 int err; 340 341 list_for_each_entry(mall_entry, &block->mall.list, list) { 342 err = mlxsw_sp_mall_port_rule_add(mlxsw_sp_port, mall_entry, 343 extack); 344 if (err) 345 goto rollback; 346 } 347 return 0; 348 349rollback: 350 list_for_each_entry_continue_reverse(mall_entry, &block->mall.list, 351 list) 352 mlxsw_sp_mall_port_rule_del(mlxsw_sp_port, mall_entry); 353 return err; 354} 355 356void mlxsw_sp_mall_port_unbind(struct mlxsw_sp_flow_block *block, 357 struct mlxsw_sp_port *mlxsw_sp_port) 358{ 359 struct mlxsw_sp_mall_entry *mall_entry; 360 361 list_for_each_entry(mall_entry, &block->mall.list, list) 362 mlxsw_sp_mall_port_rule_del(mlxsw_sp_port, mall_entry); 363} 364 365int mlxsw_sp_mall_prio_get(struct mlxsw_sp_flow_block *block, u32 chain_index, 366 unsigned int *p_min_prio, unsigned int *p_max_prio) 367{ 368 if (chain_index || list_empty(&block->mall.list)) 369 /* In case there are no matchall rules, the caller 370 * receives -ENOENT to indicate there is no need 371 * to check the priorities. 372 */ 373 return -ENOENT; 374 *p_min_prio = block->mall.min_prio; 375 *p_max_prio = block->mall.max_prio; 376 return 0; 377} 378 379static int mlxsw_sp1_mall_sample_add(struct mlxsw_sp *mlxsw_sp, 380 struct mlxsw_sp_port *mlxsw_sp_port, 381 struct mlxsw_sp_mall_entry *mall_entry, 382 struct netlink_ext_ack *extack) 383{ 384 u32 rate = mall_entry->sample.params.rate; 385 386 if (!mall_entry->ingress) { 387 NL_SET_ERR_MSG(extack, "Sampling is not supported on egress"); 388 return -EOPNOTSUPP; 389 } 390 391 if (rate > MLXSW_REG_MPSC_RATE_MAX) { 392 NL_SET_ERR_MSG(extack, "Unsupported sampling rate"); 393 return -EOPNOTSUPP; 394 } 395 396 return mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, true, rate); 397} 398 399static void mlxsw_sp1_mall_sample_del(struct mlxsw_sp *mlxsw_sp, 400 struct mlxsw_sp_port *mlxsw_sp_port, 401 struct mlxsw_sp_mall_entry *mall_entry) 402{ 403 mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, false, 1); 404} 405 406const struct mlxsw_sp_mall_ops mlxsw_sp1_mall_ops = { 407 .sample_add = mlxsw_sp1_mall_sample_add, 408 .sample_del = mlxsw_sp1_mall_sample_del, 409}; 410 411static int mlxsw_sp2_mall_sample_add(struct mlxsw_sp *mlxsw_sp, 412 struct mlxsw_sp_port *mlxsw_sp_port, 413 struct mlxsw_sp_mall_entry *mall_entry, 414 struct netlink_ext_ack *extack) 415{ 416 struct mlxsw_sp_span_trigger_parms trigger_parms = {}; 417 struct mlxsw_sp_span_agent_parms agent_parms = { 418 .to_dev = NULL, /* Mirror to CPU. */ 419 .session_id = MLXSW_SP_SPAN_SESSION_ID_SAMPLING, 420 }; 421 u32 rate = mall_entry->sample.params.rate; 422 enum mlxsw_sp_span_trigger span_trigger; 423 int err; 424 425 err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->sample.span_id, 426 &agent_parms); 427 if (err) { 428 NL_SET_ERR_MSG(extack, "Failed to get SPAN agent"); 429 return err; 430 } 431 432 err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, 433 mall_entry->ingress); 434 if (err) { 435 NL_SET_ERR_MSG(extack, "Failed to get analyzed port"); 436 goto err_analyzed_port_get; 437 } 438 439 span_trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS : 440 MLXSW_SP_SPAN_TRIGGER_EGRESS; 441 trigger_parms.span_id = mall_entry->sample.span_id; 442 trigger_parms.probability_rate = rate; 443 err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port, 444 &trigger_parms); 445 if (err) { 446 NL_SET_ERR_MSG(extack, "Failed to bind SPAN agent"); 447 goto err_agent_bind; 448 } 449 450 return 0; 451 452err_agent_bind: 453 mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress); 454err_analyzed_port_get: 455 mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->sample.span_id); 456 return err; 457} 458 459static void mlxsw_sp2_mall_sample_del(struct mlxsw_sp *mlxsw_sp, 460 struct mlxsw_sp_port *mlxsw_sp_port, 461 struct mlxsw_sp_mall_entry *mall_entry) 462{ 463 struct mlxsw_sp_span_trigger_parms trigger_parms = {}; 464 enum mlxsw_sp_span_trigger span_trigger; 465 466 span_trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS : 467 MLXSW_SP_SPAN_TRIGGER_EGRESS; 468 trigger_parms.span_id = mall_entry->sample.span_id; 469 mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port, 470 &trigger_parms); 471 mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress); 472 mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->sample.span_id); 473} 474 475const struct mlxsw_sp_mall_ops mlxsw_sp2_mall_ops = { 476 .sample_add = mlxsw_sp2_mall_sample_add, 477 .sample_del = mlxsw_sp2_mall_sample_del, 478};