root/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mlxsw_sp_rx_listener
  2. mlxsw_sp_rx_drop_listener
  3. mlxsw_sp_devlink_traps_init
  4. mlxsw_sp_devlink_traps_fini
  5. mlxsw_sp_trap_init
  6. mlxsw_sp_trap_fini
  7. mlxsw_sp_trap_action_set
  8. mlxsw_sp_trap_group_policer_init
  9. __mlxsw_sp_trap_group_init
  10. mlxsw_sp_trap_group_init

   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 #define MLXSW_SP_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT
  13 
  14 static void mlxsw_sp_rx_drop_listener(struct sk_buff *skb, u8 local_port,
  15                                       void *priv);
  16 
  17 #define MLXSW_SP_TRAP_DROP(_id, _group_id)                                    \
  18         DEVLINK_TRAP_GENERIC(DROP, DROP, _id,                                 \
  19                              DEVLINK_TRAP_GROUP_GENERIC(_group_id),           \
  20                              MLXSW_SP_TRAP_METADATA)
  21 
  22 #define MLXSW_SP_RXL_DISCARD(_id, _group_id)                                  \
  23         MLXSW_RXL(mlxsw_sp_rx_drop_listener, DISCARD_##_id, SET_FW_DEFAULT,   \
  24                   false, SP_##_group_id, DISCARD)
  25 
  26 static struct devlink_trap mlxsw_sp_traps_arr[] = {
  27         MLXSW_SP_TRAP_DROP(SMAC_MC, L2_DROPS),
  28         MLXSW_SP_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS),
  29         MLXSW_SP_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS),
  30         MLXSW_SP_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS),
  31         MLXSW_SP_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS),
  32         MLXSW_SP_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS),
  33 };
  34 
  35 static struct mlxsw_listener mlxsw_sp_listeners_arr[] = {
  36         MLXSW_SP_RXL_DISCARD(ING_PACKET_SMAC_MC, L2_DISCARDS),
  37         MLXSW_SP_RXL_DISCARD(ING_SWITCH_VTAG_ALLOW, L2_DISCARDS),
  38         MLXSW_SP_RXL_DISCARD(ING_SWITCH_VLAN, L2_DISCARDS),
  39         MLXSW_SP_RXL_DISCARD(ING_SWITCH_STP, L2_DISCARDS),
  40         MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_UC, L2_DISCARDS),
  41         MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_MC_NULL, L2_DISCARDS),
  42         MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_LB, L2_DISCARDS),
  43 };
  44 
  45 /* Mapping between hardware trap and devlink trap. Multiple hardware traps can
  46  * be mapped to the same devlink trap. Order is according to
  47  * 'mlxsw_sp_listeners_arr'.
  48  */
  49 static u16 mlxsw_sp_listener_devlink_map[] = {
  50         DEVLINK_TRAP_GENERIC_ID_SMAC_MC,
  51         DEVLINK_TRAP_GENERIC_ID_VLAN_TAG_MISMATCH,
  52         DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER,
  53         DEVLINK_TRAP_GENERIC_ID_INGRESS_STP_FILTER,
  54         DEVLINK_TRAP_GENERIC_ID_EMPTY_TX_LIST,
  55         DEVLINK_TRAP_GENERIC_ID_EMPTY_TX_LIST,
  56         DEVLINK_TRAP_GENERIC_ID_PORT_LOOPBACK_FILTER,
  57 };
  58 
  59 static int mlxsw_sp_rx_listener(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
  60                                 u8 local_port,
  61                                 struct mlxsw_sp_port *mlxsw_sp_port)
  62 {
  63         struct mlxsw_sp_port_pcpu_stats *pcpu_stats;
  64 
  65         if (unlikely(!mlxsw_sp_port)) {
  66                 dev_warn_ratelimited(mlxsw_sp->bus_info->dev, "Port %d: skb received for non-existent port\n",
  67                                      local_port);
  68                 kfree_skb(skb);
  69                 return -EINVAL;
  70         }
  71 
  72         skb->dev = mlxsw_sp_port->dev;
  73 
  74         pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats);
  75         u64_stats_update_begin(&pcpu_stats->syncp);
  76         pcpu_stats->rx_packets++;
  77         pcpu_stats->rx_bytes += skb->len;
  78         u64_stats_update_end(&pcpu_stats->syncp);
  79 
  80         skb->protocol = eth_type_trans(skb, skb->dev);
  81 
  82         return 0;
  83 }
  84 
  85 static void mlxsw_sp_rx_drop_listener(struct sk_buff *skb, u8 local_port,
  86                                       void *trap_ctx)
  87 {
  88         struct devlink_port *in_devlink_port;
  89         struct mlxsw_sp_port *mlxsw_sp_port;
  90         struct mlxsw_sp *mlxsw_sp;
  91         struct devlink *devlink;
  92 
  93         mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
  94         mlxsw_sp_port = mlxsw_sp->ports[local_port];
  95 
  96         if (mlxsw_sp_rx_listener(mlxsw_sp, skb, local_port, mlxsw_sp_port))
  97                 return;
  98 
  99         devlink = priv_to_devlink(mlxsw_sp->core);
 100         in_devlink_port = mlxsw_core_port_devlink_port_get(mlxsw_sp->core,
 101                                                            local_port);
 102         skb_push(skb, ETH_HLEN);
 103         devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port);
 104         consume_skb(skb);
 105 }
 106 
 107 int mlxsw_sp_devlink_traps_init(struct mlxsw_sp *mlxsw_sp)
 108 {
 109         struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
 110 
 111         if (WARN_ON(ARRAY_SIZE(mlxsw_sp_listener_devlink_map) !=
 112                     ARRAY_SIZE(mlxsw_sp_listeners_arr)))
 113                 return -EINVAL;
 114 
 115         return devlink_traps_register(devlink, mlxsw_sp_traps_arr,
 116                                       ARRAY_SIZE(mlxsw_sp_traps_arr),
 117                                       mlxsw_sp);
 118 }
 119 
 120 void mlxsw_sp_devlink_traps_fini(struct mlxsw_sp *mlxsw_sp)
 121 {
 122         struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
 123 
 124         devlink_traps_unregister(devlink, mlxsw_sp_traps_arr,
 125                                  ARRAY_SIZE(mlxsw_sp_traps_arr));
 126 }
 127 
 128 int mlxsw_sp_trap_init(struct mlxsw_core *mlxsw_core,
 129                        const struct devlink_trap *trap, void *trap_ctx)
 130 {
 131         int i;
 132 
 133         for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) {
 134                 struct mlxsw_listener *listener;
 135                 int err;
 136 
 137                 if (mlxsw_sp_listener_devlink_map[i] != trap->id)
 138                         continue;
 139                 listener = &mlxsw_sp_listeners_arr[i];
 140 
 141                 err = mlxsw_core_trap_register(mlxsw_core, listener, trap_ctx);
 142                 if (err)
 143                         return err;
 144         }
 145 
 146         return 0;
 147 }
 148 
 149 void mlxsw_sp_trap_fini(struct mlxsw_core *mlxsw_core,
 150                         const struct devlink_trap *trap, void *trap_ctx)
 151 {
 152         int i;
 153 
 154         for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) {
 155                 struct mlxsw_listener *listener;
 156 
 157                 if (mlxsw_sp_listener_devlink_map[i] != trap->id)
 158                         continue;
 159                 listener = &mlxsw_sp_listeners_arr[i];
 160 
 161                 mlxsw_core_trap_unregister(mlxsw_core, listener, trap_ctx);
 162         }
 163 }
 164 
 165 int mlxsw_sp_trap_action_set(struct mlxsw_core *mlxsw_core,
 166                              const struct devlink_trap *trap,
 167                              enum devlink_trap_action action)
 168 {
 169         int i;
 170 
 171         for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) {
 172                 enum mlxsw_reg_hpkt_action hw_action;
 173                 struct mlxsw_listener *listener;
 174                 int err;
 175 
 176                 if (mlxsw_sp_listener_devlink_map[i] != trap->id)
 177                         continue;
 178                 listener = &mlxsw_sp_listeners_arr[i];
 179 
 180                 switch (action) {
 181                 case DEVLINK_TRAP_ACTION_DROP:
 182                         hw_action = MLXSW_REG_HPKT_ACTION_SET_FW_DEFAULT;
 183                         break;
 184                 case DEVLINK_TRAP_ACTION_TRAP:
 185                         hw_action = MLXSW_REG_HPKT_ACTION_TRAP_EXCEPTION_TO_CPU;
 186                         break;
 187                 default:
 188                         return -EINVAL;
 189                 }
 190 
 191                 err = mlxsw_core_trap_action_set(mlxsw_core, listener,
 192                                                  hw_action);
 193                 if (err)
 194                         return err;
 195         }
 196 
 197         return 0;
 198 }
 199 
 200 #define MLXSW_SP_DISCARD_POLICER_ID     (MLXSW_REG_HTGT_TRAP_GROUP_MAX + 1)
 201 
 202 static int
 203 mlxsw_sp_trap_group_policer_init(struct mlxsw_sp *mlxsw_sp,
 204                                  const struct devlink_trap_group *group)
 205 {
 206         enum mlxsw_reg_qpcr_ir_units ir_units;
 207         char qpcr_pl[MLXSW_REG_QPCR_LEN];
 208         u16 policer_id;
 209         u8 burst_size;
 210         bool is_bytes;
 211         u32 rate;
 212 
 213         switch (group->id) {
 214         case DEVLINK_TRAP_GROUP_GENERIC_ID_L2_DROPS:
 215                 policer_id = MLXSW_SP_DISCARD_POLICER_ID;
 216                 ir_units = MLXSW_REG_QPCR_IR_UNITS_M;
 217                 is_bytes = false;
 218                 rate = 10 * 1024; /* 10Kpps */
 219                 burst_size = 7;
 220                 break;
 221         default:
 222                 return -EINVAL;
 223         }
 224 
 225         mlxsw_reg_qpcr_pack(qpcr_pl, policer_id, ir_units, is_bytes, rate,
 226                             burst_size);
 227         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
 228 }
 229 
 230 static int
 231 __mlxsw_sp_trap_group_init(struct mlxsw_sp *mlxsw_sp,
 232                            const struct devlink_trap_group *group)
 233 {
 234         char htgt_pl[MLXSW_REG_HTGT_LEN];
 235         u8 priority, tc, group_id;
 236         u16 policer_id;
 237 
 238         switch (group->id) {
 239         case DEVLINK_TRAP_GROUP_GENERIC_ID_L2_DROPS:
 240                 group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L2_DISCARDS;
 241                 policer_id = MLXSW_SP_DISCARD_POLICER_ID;
 242                 priority = 0;
 243                 tc = 1;
 244                 break;
 245         default:
 246                 return -EINVAL;
 247         }
 248 
 249         mlxsw_reg_htgt_pack(htgt_pl, group_id, policer_id, priority, tc);
 250         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl);
 251 }
 252 
 253 int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
 254                              const struct devlink_trap_group *group)
 255 {
 256         struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
 257         int err;
 258 
 259         err = mlxsw_sp_trap_group_policer_init(mlxsw_sp, group);
 260         if (err)
 261                 return err;
 262 
 263         err = __mlxsw_sp_trap_group_init(mlxsw_sp, group);
 264         if (err)
 265                 return err;
 266 
 267         return 0;
 268 }

/* [<][>][^][v][top][bottom][index][help] */