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 13 enum mlxsw_sp_mall_action_type { 14 MLXSW_SP_MALL_ACTION_TYPE_MIRROR, 15 MLXSW_SP_MALL_ACTION_TYPE_SAMPLE, 16 }; 17 18 struct mlxsw_sp_mall_mirror_entry { 19 const struct net_device *to_dev; 20 int span_id; 21 }; 22 23 struct mlxsw_sp_mall_entry { 24 struct list_head list; 25 unsigned long cookie; 26 unsigned int priority; 27 enum mlxsw_sp_mall_action_type type; 28 bool ingress; 29 union { 30 struct mlxsw_sp_mall_mirror_entry mirror; 31 struct mlxsw_sp_port_sample sample; 32 }; 33 struct rcu_head rcu; 34 }; 35 36 static struct mlxsw_sp_mall_entry * 37 mlxsw_sp_mall_entry_find(struct mlxsw_sp_flow_block *block, unsigned long cookie) 38 { 39 struct mlxsw_sp_mall_entry *mall_entry; 40 41 list_for_each_entry(mall_entry, &block->mall.list, list) 42 if (mall_entry->cookie == cookie) 43 return mall_entry; 44 45 return NULL; 46 } 47 48 static int 49 mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port, 50 struct mlxsw_sp_mall_entry *mall_entry) 51 { 52 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 53 struct mlxsw_sp_span_trigger_parms parms; 54 enum mlxsw_sp_span_trigger trigger; 55 int err; 56 57 if (!mall_entry->mirror.to_dev) { 58 netdev_err(mlxsw_sp_port->dev, "Could not find requested device\n"); 59 return -EINVAL; 60 } 61 62 err = mlxsw_sp_span_agent_get(mlxsw_sp, mall_entry->mirror.to_dev, 63 &mall_entry->mirror.span_id); 64 if (err) 65 return err; 66 67 err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, 68 mall_entry->ingress); 69 if (err) 70 goto err_analyzed_port_get; 71 72 trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS : 73 MLXSW_SP_SPAN_TRIGGER_EGRESS; 74 parms.span_id = mall_entry->mirror.span_id; 75 err = mlxsw_sp_span_agent_bind(mlxsw_sp, trigger, mlxsw_sp_port, 76 &parms); 77 if (err) 78 goto err_agent_bind; 79 80 return 0; 81 82 err_agent_bind: 83 mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress); 84 err_analyzed_port_get: 85 mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->mirror.span_id); 86 return err; 87 } 88 89 static void 90 mlxsw_sp_mall_port_mirror_del(struct mlxsw_sp_port *mlxsw_sp_port, 91 struct mlxsw_sp_mall_entry *mall_entry) 92 { 93 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 94 struct mlxsw_sp_span_trigger_parms parms; 95 enum mlxsw_sp_span_trigger trigger; 96 97 trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS : 98 MLXSW_SP_SPAN_TRIGGER_EGRESS; 99 parms.span_id = mall_entry->mirror.span_id; 100 mlxsw_sp_span_agent_unbind(mlxsw_sp, trigger, mlxsw_sp_port, &parms); 101 mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress); 102 mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->mirror.span_id); 103 } 104 105 static int mlxsw_sp_mall_port_sample_set(struct mlxsw_sp_port *mlxsw_sp_port, 106 bool enable, u32 rate) 107 { 108 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 109 char mpsc_pl[MLXSW_REG_MPSC_LEN]; 110 111 mlxsw_reg_mpsc_pack(mpsc_pl, mlxsw_sp_port->local_port, enable, rate); 112 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpsc), mpsc_pl); 113 } 114 115 static int 116 mlxsw_sp_mall_port_sample_add(struct mlxsw_sp_port *mlxsw_sp_port, 117 struct mlxsw_sp_mall_entry *mall_entry) 118 { 119 int err; 120 121 if (rtnl_dereference(mlxsw_sp_port->sample)) { 122 netdev_err(mlxsw_sp_port->dev, "sample already active\n"); 123 return -EEXIST; 124 } 125 rcu_assign_pointer(mlxsw_sp_port->sample, &mall_entry->sample); 126 127 err = mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, true, 128 mall_entry->sample.rate); 129 if (err) 130 goto err_port_sample_set; 131 return 0; 132 133 err_port_sample_set: 134 RCU_INIT_POINTER(mlxsw_sp_port->sample, NULL); 135 return err; 136 } 137 138 static void 139 mlxsw_sp_mall_port_sample_del(struct mlxsw_sp_port *mlxsw_sp_port) 140 { 141 if (!mlxsw_sp_port->sample) 142 return; 143 144 mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, false, 1); 145 RCU_INIT_POINTER(mlxsw_sp_port->sample, NULL); 146 } 147 148 static int 149 mlxsw_sp_mall_port_rule_add(struct mlxsw_sp_port *mlxsw_sp_port, 150 struct mlxsw_sp_mall_entry *mall_entry) 151 { 152 switch (mall_entry->type) { 153 case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 154 return mlxsw_sp_mall_port_mirror_add(mlxsw_sp_port, mall_entry); 155 case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE: 156 return mlxsw_sp_mall_port_sample_add(mlxsw_sp_port, mall_entry); 157 default: 158 WARN_ON(1); 159 return -EINVAL; 160 } 161 } 162 163 static void 164 mlxsw_sp_mall_port_rule_del(struct mlxsw_sp_port *mlxsw_sp_port, 165 struct mlxsw_sp_mall_entry *mall_entry) 166 { 167 switch (mall_entry->type) { 168 case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 169 mlxsw_sp_mall_port_mirror_del(mlxsw_sp_port, mall_entry); 170 break; 171 case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE: 172 mlxsw_sp_mall_port_sample_del(mlxsw_sp_port); 173 break; 174 default: 175 WARN_ON(1); 176 } 177 } 178 179 static void mlxsw_sp_mall_prio_update(struct mlxsw_sp_flow_block *block) 180 { 181 struct mlxsw_sp_mall_entry *mall_entry; 182 183 if (list_empty(&block->mall.list)) 184 return; 185 block->mall.min_prio = UINT_MAX; 186 block->mall.max_prio = 0; 187 list_for_each_entry(mall_entry, &block->mall.list, list) { 188 if (mall_entry->priority < block->mall.min_prio) 189 block->mall.min_prio = mall_entry->priority; 190 if (mall_entry->priority > block->mall.max_prio) 191 block->mall.max_prio = mall_entry->priority; 192 } 193 } 194 195 int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp, 196 struct mlxsw_sp_flow_block *block, 197 struct tc_cls_matchall_offload *f) 198 { 199 struct mlxsw_sp_flow_block_binding *binding; 200 struct mlxsw_sp_mall_entry *mall_entry; 201 __be16 protocol = f->common.protocol; 202 struct flow_action_entry *act; 203 unsigned int flower_min_prio; 204 unsigned int flower_max_prio; 205 bool flower_prio_valid; 206 int err; 207 208 if (!flow_offload_has_one_action(&f->rule->action)) { 209 NL_SET_ERR_MSG(f->common.extack, "Only singular actions are supported"); 210 return -EOPNOTSUPP; 211 } 212 213 if (f->common.chain_index) { 214 NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported"); 215 return -EOPNOTSUPP; 216 } 217 218 if (mlxsw_sp_flow_block_is_mixed_bound(block)) { 219 NL_SET_ERR_MSG(f->common.extack, "Only not mixed bound blocks are supported"); 220 return -EOPNOTSUPP; 221 } 222 223 err = mlxsw_sp_flower_prio_get(mlxsw_sp, block, f->common.chain_index, 224 &flower_min_prio, &flower_max_prio); 225 if (err) { 226 if (err != -ENOENT) { 227 NL_SET_ERR_MSG(f->common.extack, "Failed to get flower priorities"); 228 return err; 229 } 230 flower_prio_valid = false; 231 /* No flower filters are installed in specified chain. */ 232 } else { 233 flower_prio_valid = true; 234 } 235 236 mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL); 237 if (!mall_entry) 238 return -ENOMEM; 239 mall_entry->cookie = f->cookie; 240 mall_entry->priority = f->common.prio; 241 mall_entry->ingress = mlxsw_sp_flow_block_is_ingress_bound(block); 242 243 act = &f->rule->action.entries[0]; 244 245 if (act->id == FLOW_ACTION_MIRRED && protocol == htons(ETH_P_ALL)) { 246 if (flower_prio_valid && mall_entry->ingress && 247 mall_entry->priority >= flower_min_prio) { 248 NL_SET_ERR_MSG(f->common.extack, "Failed to add behind existing flower rules"); 249 err = -EOPNOTSUPP; 250 goto errout; 251 } 252 if (flower_prio_valid && !mall_entry->ingress && 253 mall_entry->priority <= flower_max_prio) { 254 NL_SET_ERR_MSG(f->common.extack, "Failed to add in front of existing flower rules"); 255 err = -EOPNOTSUPP; 256 goto errout; 257 } 258 mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR; 259 mall_entry->mirror.to_dev = act->dev; 260 } else if (act->id == FLOW_ACTION_SAMPLE && 261 protocol == htons(ETH_P_ALL)) { 262 if (!mall_entry->ingress) { 263 NL_SET_ERR_MSG(f->common.extack, "Sample is not supported on egress"); 264 err = -EOPNOTSUPP; 265 goto errout; 266 } 267 if (flower_prio_valid && 268 mall_entry->priority >= flower_min_prio) { 269 NL_SET_ERR_MSG(f->common.extack, "Failed to add behind existing flower rules"); 270 err = -EOPNOTSUPP; 271 goto errout; 272 } 273 if (act->sample.rate > MLXSW_REG_MPSC_RATE_MAX) { 274 NL_SET_ERR_MSG(f->common.extack, "Sample rate not supported"); 275 err = -EOPNOTSUPP; 276 goto errout; 277 } 278 mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_SAMPLE; 279 mall_entry->sample.psample_group = act->sample.psample_group; 280 mall_entry->sample.truncate = act->sample.truncate; 281 mall_entry->sample.trunc_size = act->sample.trunc_size; 282 mall_entry->sample.rate = act->sample.rate; 283 } else { 284 err = -EOPNOTSUPP; 285 goto errout; 286 } 287 288 list_for_each_entry(binding, &block->binding_list, list) { 289 err = mlxsw_sp_mall_port_rule_add(binding->mlxsw_sp_port, 290 mall_entry); 291 if (err) 292 goto rollback; 293 } 294 295 block->rule_count++; 296 if (mall_entry->ingress) 297 block->egress_blocker_rule_count++; 298 else 299 block->ingress_blocker_rule_count++; 300 list_add_tail(&mall_entry->list, &block->mall.list); 301 mlxsw_sp_mall_prio_update(block); 302 return 0; 303 304 rollback: 305 list_for_each_entry_continue_reverse(binding, &block->binding_list, 306 list) 307 mlxsw_sp_mall_port_rule_del(binding->mlxsw_sp_port, mall_entry); 308 errout: 309 kfree(mall_entry); 310 return err; 311 } 312 313 void mlxsw_sp_mall_destroy(struct mlxsw_sp_flow_block *block, 314 struct tc_cls_matchall_offload *f) 315 { 316 struct mlxsw_sp_flow_block_binding *binding; 317 struct mlxsw_sp_mall_entry *mall_entry; 318 319 mall_entry = mlxsw_sp_mall_entry_find(block, f->cookie); 320 if (!mall_entry) { 321 NL_SET_ERR_MSG(f->common.extack, "Entry not found"); 322 return; 323 } 324 325 list_del(&mall_entry->list); 326 if (mall_entry->ingress) 327 block->egress_blocker_rule_count--; 328 else 329 block->ingress_blocker_rule_count--; 330 block->rule_count--; 331 list_for_each_entry(binding, &block->binding_list, list) 332 mlxsw_sp_mall_port_rule_del(binding->mlxsw_sp_port, mall_entry); 333 kfree_rcu(mall_entry, rcu); /* sample RX packets may be in-flight */ 334 mlxsw_sp_mall_prio_update(block); 335 } 336 337 int mlxsw_sp_mall_port_bind(struct mlxsw_sp_flow_block *block, 338 struct mlxsw_sp_port *mlxsw_sp_port) 339 { 340 struct mlxsw_sp_mall_entry *mall_entry; 341 int err; 342 343 list_for_each_entry(mall_entry, &block->mall.list, list) { 344 err = mlxsw_sp_mall_port_rule_add(mlxsw_sp_port, mall_entry); 345 if (err) 346 goto rollback; 347 } 348 return 0; 349 350 rollback: 351 list_for_each_entry_continue_reverse(mall_entry, &block->mall.list, 352 list) 353 mlxsw_sp_mall_port_rule_del(mlxsw_sp_port, mall_entry); 354 return err; 355 } 356 357 void mlxsw_sp_mall_port_unbind(struct mlxsw_sp_flow_block *block, 358 struct mlxsw_sp_port *mlxsw_sp_port) 359 { 360 struct mlxsw_sp_mall_entry *mall_entry; 361 362 list_for_each_entry(mall_entry, &block->mall.list, list) 363 mlxsw_sp_mall_port_rule_del(mlxsw_sp_port, mall_entry); 364 } 365 366 int mlxsw_sp_mall_prio_get(struct mlxsw_sp_flow_block *block, u32 chain_index, 367 unsigned int *p_min_prio, unsigned int *p_max_prio) 368 { 369 if (chain_index || list_empty(&block->mall.list)) 370 /* In case there are no matchall rules, the caller 371 * receives -ENOENT to indicate there is no need 372 * to check the priorities. 373 */ 374 return -ENOENT; 375 *p_min_prio = block->mall.min_prio; 376 *p_max_prio = block->mall.max_prio; 377 return 0; 378 } 379