1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /* Copyright (c) 2019 Mellanox Technologies. All rights reserved */ 3 4 #include <linux/kernel.h> 5 #include <net/devlink.h> 6 #include <uapi/linux/devlink.h> 7 8 #include "core.h" 9 #include "reg.h" 10 #include "spectrum.h" 11 12 /* All driver-specific traps must be documented in 13 * Documentation/networking/devlink/mlxsw.rst 14 */ 15 enum { 16 DEVLINK_MLXSW_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX, 17 DEVLINK_MLXSW_TRAP_ID_IRIF_DISABLED, 18 DEVLINK_MLXSW_TRAP_ID_ERIF_DISABLED, 19 }; 20 21 #define DEVLINK_MLXSW_TRAP_NAME_IRIF_DISABLED \ 22 "irif_disabled" 23 #define DEVLINK_MLXSW_TRAP_NAME_ERIF_DISABLED \ 24 "erif_disabled" 25 26 #define MLXSW_SP_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT 27 28 static void mlxsw_sp_rx_drop_listener(struct sk_buff *skb, u8 local_port, 29 void *priv); 30 static void mlxsw_sp_rx_exception_listener(struct sk_buff *skb, u8 local_port, 31 void *trap_ctx); 32 33 #define MLXSW_SP_TRAP_DROP(_id, _group_id) \ 34 DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ 35 DEVLINK_TRAP_GROUP_GENERIC(_group_id), \ 36 MLXSW_SP_TRAP_METADATA) 37 38 #define MLXSW_SP_TRAP_DRIVER_DROP(_id, _group_id) \ 39 DEVLINK_TRAP_DRIVER(DROP, DROP, DEVLINK_MLXSW_TRAP_ID_##_id, \ 40 DEVLINK_MLXSW_TRAP_NAME_##_id, \ 41 DEVLINK_TRAP_GROUP_GENERIC(_group_id), \ 42 MLXSW_SP_TRAP_METADATA) 43 44 #define MLXSW_SP_TRAP_EXCEPTION(_id, _group_id) \ 45 DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id, \ 46 DEVLINK_TRAP_GROUP_GENERIC(_group_id), \ 47 MLXSW_SP_TRAP_METADATA) 48 49 #define MLXSW_SP_RXL_DISCARD(_id, _group_id) \ 50 MLXSW_RXL(mlxsw_sp_rx_drop_listener, DISCARD_##_id, SET_FW_DEFAULT, \ 51 false, SP_##_group_id, DISCARD) 52 53 #define MLXSW_SP_RXL_EXCEPTION(_id, _group_id, _action) \ 54 MLXSW_RXL(mlxsw_sp_rx_exception_listener, _id, \ 55 _action, false, SP_##_group_id, DISCARD) 56 57 static struct devlink_trap mlxsw_sp_traps_arr[] = { 58 MLXSW_SP_TRAP_DROP(SMAC_MC, L2_DROPS), 59 MLXSW_SP_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS), 60 MLXSW_SP_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS), 61 MLXSW_SP_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS), 62 MLXSW_SP_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS), 63 MLXSW_SP_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS), 64 MLXSW_SP_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS), 65 MLXSW_SP_TRAP_DROP(NON_IP_PACKET, L3_DROPS), 66 MLXSW_SP_TRAP_DROP(UC_DIP_MC_DMAC, L3_DROPS), 67 MLXSW_SP_TRAP_DROP(DIP_LB, L3_DROPS), 68 MLXSW_SP_TRAP_DROP(SIP_MC, L3_DROPS), 69 MLXSW_SP_TRAP_DROP(SIP_LB, L3_DROPS), 70 MLXSW_SP_TRAP_DROP(CORRUPTED_IP_HDR, L3_DROPS), 71 MLXSW_SP_TRAP_DROP(IPV4_SIP_BC, L3_DROPS), 72 MLXSW_SP_TRAP_DROP(IPV6_MC_DIP_RESERVED_SCOPE, L3_DROPS), 73 MLXSW_SP_TRAP_DROP(IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE, L3_DROPS), 74 MLXSW_SP_TRAP_EXCEPTION(MTU_ERROR, L3_DROPS), 75 MLXSW_SP_TRAP_EXCEPTION(TTL_ERROR, L3_DROPS), 76 MLXSW_SP_TRAP_EXCEPTION(RPF, L3_DROPS), 77 MLXSW_SP_TRAP_EXCEPTION(REJECT_ROUTE, L3_DROPS), 78 MLXSW_SP_TRAP_EXCEPTION(UNRESOLVED_NEIGH, L3_DROPS), 79 MLXSW_SP_TRAP_EXCEPTION(IPV4_LPM_UNICAST_MISS, L3_DROPS), 80 MLXSW_SP_TRAP_EXCEPTION(IPV6_LPM_UNICAST_MISS, L3_DROPS), 81 MLXSW_SP_TRAP_DRIVER_DROP(IRIF_DISABLED, L3_DROPS), 82 MLXSW_SP_TRAP_DRIVER_DROP(ERIF_DISABLED, L3_DROPS), 83 MLXSW_SP_TRAP_DROP(NON_ROUTABLE, L3_DROPS), 84 MLXSW_SP_TRAP_EXCEPTION(DECAP_ERROR, TUNNEL_DROPS), 85 MLXSW_SP_TRAP_DROP(OVERLAY_SMAC_MC, TUNNEL_DROPS), 86 }; 87 88 static struct mlxsw_listener mlxsw_sp_listeners_arr[] = { 89 MLXSW_SP_RXL_DISCARD(ING_PACKET_SMAC_MC, L2_DISCARDS), 90 MLXSW_SP_RXL_DISCARD(ING_SWITCH_VTAG_ALLOW, L2_DISCARDS), 91 MLXSW_SP_RXL_DISCARD(ING_SWITCH_VLAN, L2_DISCARDS), 92 MLXSW_SP_RXL_DISCARD(ING_SWITCH_STP, L2_DISCARDS), 93 MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_UC, L2_DISCARDS), 94 MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_MC_NULL, L2_DISCARDS), 95 MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_LB, L2_DISCARDS), 96 MLXSW_SP_RXL_DISCARD(ROUTER2, L3_DISCARDS), 97 MLXSW_SP_RXL_DISCARD(ING_ROUTER_NON_IP_PACKET, L3_DISCARDS), 98 MLXSW_SP_RXL_DISCARD(ING_ROUTER_UC_DIP_MC_DMAC, L3_DISCARDS), 99 MLXSW_SP_RXL_DISCARD(ING_ROUTER_DIP_LB, L3_DISCARDS), 100 MLXSW_SP_RXL_DISCARD(ING_ROUTER_SIP_MC, L3_DISCARDS), 101 MLXSW_SP_RXL_DISCARD(ING_ROUTER_SIP_LB, L3_DISCARDS), 102 MLXSW_SP_RXL_DISCARD(ING_ROUTER_CORRUPTED_IP_HDR, L3_DISCARDS), 103 MLXSW_SP_RXL_DISCARD(ING_ROUTER_IPV4_SIP_BC, L3_DISCARDS), 104 MLXSW_SP_RXL_DISCARD(IPV6_MC_DIP_RESERVED_SCOPE, L3_DISCARDS), 105 MLXSW_SP_RXL_DISCARD(IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE, L3_DISCARDS), 106 MLXSW_SP_RXL_EXCEPTION(MTUERROR, ROUTER_EXP, TRAP_TO_CPU), 107 MLXSW_SP_RXL_EXCEPTION(TTLERROR, ROUTER_EXP, TRAP_TO_CPU), 108 MLXSW_SP_RXL_EXCEPTION(RPF, RPF, TRAP_TO_CPU), 109 MLXSW_SP_RXL_EXCEPTION(RTR_INGRESS1, REMOTE_ROUTE, TRAP_TO_CPU), 110 MLXSW_SP_RXL_EXCEPTION(HOST_MISS_IPV4, HOST_MISS, TRAP_TO_CPU), 111 MLXSW_SP_RXL_EXCEPTION(HOST_MISS_IPV6, HOST_MISS, TRAP_TO_CPU), 112 MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER3, REMOTE_ROUTE, 113 TRAP_EXCEPTION_TO_CPU), 114 MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER_LPM4, ROUTER_EXP, 115 TRAP_EXCEPTION_TO_CPU), 116 MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER_LPM6, ROUTER_EXP, 117 TRAP_EXCEPTION_TO_CPU), 118 MLXSW_SP_RXL_DISCARD(ROUTER_IRIF_EN, L3_DISCARDS), 119 MLXSW_SP_RXL_DISCARD(ROUTER_ERIF_EN, L3_DISCARDS), 120 MLXSW_SP_RXL_DISCARD(NON_ROUTABLE, L3_DISCARDS), 121 MLXSW_SP_RXL_EXCEPTION(DECAP_ECN0, ROUTER_EXP, TRAP_EXCEPTION_TO_CPU), 122 MLXSW_SP_RXL_EXCEPTION(IPIP_DECAP_ERROR, ROUTER_EXP, 123 TRAP_EXCEPTION_TO_CPU), 124 MLXSW_SP_RXL_EXCEPTION(DISCARD_DEC_PKT, TUNNEL_DISCARDS, 125 TRAP_EXCEPTION_TO_CPU), 126 MLXSW_SP_RXL_DISCARD(OVERLAY_SMAC_MC, TUNNEL_DISCARDS), 127 }; 128 129 /* Mapping between hardware trap and devlink trap. Multiple hardware traps can 130 * be mapped to the same devlink trap. Order is according to 131 * 'mlxsw_sp_listeners_arr'. 132 */ 133 static u16 mlxsw_sp_listener_devlink_map[] = { 134 DEVLINK_TRAP_GENERIC_ID_SMAC_MC, 135 DEVLINK_TRAP_GENERIC_ID_VLAN_TAG_MISMATCH, 136 DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER, 137 DEVLINK_TRAP_GENERIC_ID_INGRESS_STP_FILTER, 138 DEVLINK_TRAP_GENERIC_ID_EMPTY_TX_LIST, 139 DEVLINK_TRAP_GENERIC_ID_EMPTY_TX_LIST, 140 DEVLINK_TRAP_GENERIC_ID_PORT_LOOPBACK_FILTER, 141 DEVLINK_TRAP_GENERIC_ID_BLACKHOLE_ROUTE, 142 DEVLINK_TRAP_GENERIC_ID_NON_IP_PACKET, 143 DEVLINK_TRAP_GENERIC_ID_UC_DIP_MC_DMAC, 144 DEVLINK_TRAP_GENERIC_ID_DIP_LB, 145 DEVLINK_TRAP_GENERIC_ID_SIP_MC, 146 DEVLINK_TRAP_GENERIC_ID_SIP_LB, 147 DEVLINK_TRAP_GENERIC_ID_CORRUPTED_IP_HDR, 148 DEVLINK_TRAP_GENERIC_ID_IPV4_SIP_BC, 149 DEVLINK_TRAP_GENERIC_ID_IPV6_MC_DIP_RESERVED_SCOPE, 150 DEVLINK_TRAP_GENERIC_ID_IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE, 151 DEVLINK_TRAP_GENERIC_ID_MTU_ERROR, 152 DEVLINK_TRAP_GENERIC_ID_TTL_ERROR, 153 DEVLINK_TRAP_GENERIC_ID_RPF, 154 DEVLINK_TRAP_GENERIC_ID_REJECT_ROUTE, 155 DEVLINK_TRAP_GENERIC_ID_UNRESOLVED_NEIGH, 156 DEVLINK_TRAP_GENERIC_ID_UNRESOLVED_NEIGH, 157 DEVLINK_TRAP_GENERIC_ID_UNRESOLVED_NEIGH, 158 DEVLINK_TRAP_GENERIC_ID_IPV4_LPM_UNICAST_MISS, 159 DEVLINK_TRAP_GENERIC_ID_IPV6_LPM_UNICAST_MISS, 160 DEVLINK_MLXSW_TRAP_ID_IRIF_DISABLED, 161 DEVLINK_MLXSW_TRAP_ID_ERIF_DISABLED, 162 DEVLINK_TRAP_GENERIC_ID_NON_ROUTABLE, 163 DEVLINK_TRAP_GENERIC_ID_DECAP_ERROR, 164 DEVLINK_TRAP_GENERIC_ID_DECAP_ERROR, 165 DEVLINK_TRAP_GENERIC_ID_DECAP_ERROR, 166 DEVLINK_TRAP_GENERIC_ID_OVERLAY_SMAC_MC, 167 }; 168 169 static int mlxsw_sp_rx_listener(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb, 170 u8 local_port, 171 struct mlxsw_sp_port *mlxsw_sp_port) 172 { 173 struct mlxsw_sp_port_pcpu_stats *pcpu_stats; 174 175 if (unlikely(!mlxsw_sp_port)) { 176 dev_warn_ratelimited(mlxsw_sp->bus_info->dev, "Port %d: skb received for non-existent port\n", 177 local_port); 178 kfree_skb(skb); 179 return -EINVAL; 180 } 181 182 skb->dev = mlxsw_sp_port->dev; 183 184 pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats); 185 u64_stats_update_begin(&pcpu_stats->syncp); 186 pcpu_stats->rx_packets++; 187 pcpu_stats->rx_bytes += skb->len; 188 u64_stats_update_end(&pcpu_stats->syncp); 189 190 skb->protocol = eth_type_trans(skb, skb->dev); 191 192 return 0; 193 } 194 195 static void mlxsw_sp_rx_drop_listener(struct sk_buff *skb, u8 local_port, 196 void *trap_ctx) 197 { 198 struct devlink_port *in_devlink_port; 199 struct mlxsw_sp_port *mlxsw_sp_port; 200 struct mlxsw_sp *mlxsw_sp; 201 struct devlink *devlink; 202 203 mlxsw_sp = devlink_trap_ctx_priv(trap_ctx); 204 mlxsw_sp_port = mlxsw_sp->ports[local_port]; 205 206 if (mlxsw_sp_rx_listener(mlxsw_sp, skb, local_port, mlxsw_sp_port)) 207 return; 208 209 devlink = priv_to_devlink(mlxsw_sp->core); 210 in_devlink_port = mlxsw_core_port_devlink_port_get(mlxsw_sp->core, 211 local_port); 212 skb_push(skb, ETH_HLEN); 213 devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port); 214 consume_skb(skb); 215 } 216 217 static void mlxsw_sp_rx_exception_listener(struct sk_buff *skb, u8 local_port, 218 void *trap_ctx) 219 { 220 struct devlink_port *in_devlink_port; 221 struct mlxsw_sp_port *mlxsw_sp_port; 222 struct mlxsw_sp *mlxsw_sp; 223 struct devlink *devlink; 224 225 mlxsw_sp = devlink_trap_ctx_priv(trap_ctx); 226 mlxsw_sp_port = mlxsw_sp->ports[local_port]; 227 228 if (mlxsw_sp_rx_listener(mlxsw_sp, skb, local_port, mlxsw_sp_port)) 229 return; 230 231 devlink = priv_to_devlink(mlxsw_sp->core); 232 in_devlink_port = mlxsw_core_port_devlink_port_get(mlxsw_sp->core, 233 local_port); 234 skb_push(skb, ETH_HLEN); 235 devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port); 236 skb_pull(skb, ETH_HLEN); 237 skb->offload_fwd_mark = 1; 238 netif_receive_skb(skb); 239 } 240 241 int mlxsw_sp_devlink_traps_init(struct mlxsw_sp *mlxsw_sp) 242 { 243 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 244 245 if (WARN_ON(ARRAY_SIZE(mlxsw_sp_listener_devlink_map) != 246 ARRAY_SIZE(mlxsw_sp_listeners_arr))) 247 return -EINVAL; 248 249 return devlink_traps_register(devlink, mlxsw_sp_traps_arr, 250 ARRAY_SIZE(mlxsw_sp_traps_arr), 251 mlxsw_sp); 252 } 253 254 void mlxsw_sp_devlink_traps_fini(struct mlxsw_sp *mlxsw_sp) 255 { 256 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 257 258 devlink_traps_unregister(devlink, mlxsw_sp_traps_arr, 259 ARRAY_SIZE(mlxsw_sp_traps_arr)); 260 } 261 262 int mlxsw_sp_trap_init(struct mlxsw_core *mlxsw_core, 263 const struct devlink_trap *trap, void *trap_ctx) 264 { 265 int i; 266 267 for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) { 268 struct mlxsw_listener *listener; 269 int err; 270 271 if (mlxsw_sp_listener_devlink_map[i] != trap->id) 272 continue; 273 listener = &mlxsw_sp_listeners_arr[i]; 274 275 err = mlxsw_core_trap_register(mlxsw_core, listener, trap_ctx); 276 if (err) 277 return err; 278 } 279 280 return 0; 281 } 282 283 void mlxsw_sp_trap_fini(struct mlxsw_core *mlxsw_core, 284 const struct devlink_trap *trap, void *trap_ctx) 285 { 286 int i; 287 288 for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) { 289 struct mlxsw_listener *listener; 290 291 if (mlxsw_sp_listener_devlink_map[i] != trap->id) 292 continue; 293 listener = &mlxsw_sp_listeners_arr[i]; 294 295 mlxsw_core_trap_unregister(mlxsw_core, listener, trap_ctx); 296 } 297 } 298 299 int mlxsw_sp_trap_action_set(struct mlxsw_core *mlxsw_core, 300 const struct devlink_trap *trap, 301 enum devlink_trap_action action) 302 { 303 int i; 304 305 for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) { 306 enum mlxsw_reg_hpkt_action hw_action; 307 struct mlxsw_listener *listener; 308 int err; 309 310 if (mlxsw_sp_listener_devlink_map[i] != trap->id) 311 continue; 312 listener = &mlxsw_sp_listeners_arr[i]; 313 314 switch (action) { 315 case DEVLINK_TRAP_ACTION_DROP: 316 hw_action = MLXSW_REG_HPKT_ACTION_SET_FW_DEFAULT; 317 break; 318 case DEVLINK_TRAP_ACTION_TRAP: 319 hw_action = MLXSW_REG_HPKT_ACTION_TRAP_EXCEPTION_TO_CPU; 320 break; 321 default: 322 return -EINVAL; 323 } 324 325 err = mlxsw_core_trap_action_set(mlxsw_core, listener, 326 hw_action); 327 if (err) 328 return err; 329 } 330 331 return 0; 332 } 333 334 #define MLXSW_SP_DISCARD_POLICER_ID (MLXSW_REG_HTGT_TRAP_GROUP_MAX + 1) 335 336 static int 337 mlxsw_sp_trap_group_policer_init(struct mlxsw_sp *mlxsw_sp, 338 const struct devlink_trap_group *group) 339 { 340 enum mlxsw_reg_qpcr_ir_units ir_units; 341 char qpcr_pl[MLXSW_REG_QPCR_LEN]; 342 u16 policer_id; 343 u8 burst_size; 344 bool is_bytes; 345 u32 rate; 346 347 switch (group->id) { 348 case DEVLINK_TRAP_GROUP_GENERIC_ID_L2_DROPS: /* fall through */ 349 case DEVLINK_TRAP_GROUP_GENERIC_ID_L3_DROPS: /* fall through */ 350 case DEVLINK_TRAP_GROUP_GENERIC_ID_TUNNEL_DROPS: 351 policer_id = MLXSW_SP_DISCARD_POLICER_ID; 352 ir_units = MLXSW_REG_QPCR_IR_UNITS_M; 353 is_bytes = false; 354 rate = 10 * 1024; /* 10Kpps */ 355 burst_size = 7; 356 break; 357 default: 358 return -EINVAL; 359 } 360 361 mlxsw_reg_qpcr_pack(qpcr_pl, policer_id, ir_units, is_bytes, rate, 362 burst_size); 363 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl); 364 } 365 366 static int 367 __mlxsw_sp_trap_group_init(struct mlxsw_sp *mlxsw_sp, 368 const struct devlink_trap_group *group) 369 { 370 char htgt_pl[MLXSW_REG_HTGT_LEN]; 371 u8 priority, tc, group_id; 372 u16 policer_id; 373 374 switch (group->id) { 375 case DEVLINK_TRAP_GROUP_GENERIC_ID_L2_DROPS: 376 group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L2_DISCARDS; 377 policer_id = MLXSW_SP_DISCARD_POLICER_ID; 378 priority = 0; 379 tc = 1; 380 break; 381 case DEVLINK_TRAP_GROUP_GENERIC_ID_L3_DROPS: 382 group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L3_DISCARDS; 383 policer_id = MLXSW_SP_DISCARD_POLICER_ID; 384 priority = 0; 385 tc = 1; 386 break; 387 case DEVLINK_TRAP_GROUP_GENERIC_ID_TUNNEL_DROPS: 388 group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_TUNNEL_DISCARDS; 389 policer_id = MLXSW_SP_DISCARD_POLICER_ID; 390 priority = 0; 391 tc = 1; 392 break; 393 default: 394 return -EINVAL; 395 } 396 397 mlxsw_reg_htgt_pack(htgt_pl, group_id, policer_id, priority, tc); 398 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl); 399 } 400 401 int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core, 402 const struct devlink_trap_group *group) 403 { 404 struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); 405 int err; 406 407 err = mlxsw_sp_trap_group_policer_init(mlxsw_sp, group); 408 if (err) 409 return err; 410 411 err = __mlxsw_sp_trap_group_init(mlxsw_sp, group); 412 if (err) 413 return err; 414 415 return 0; 416 } 417