This source file includes following definitions.
- mlxsw_sp_rx_listener
- mlxsw_sp_rx_drop_listener
- mlxsw_sp_devlink_traps_init
- mlxsw_sp_devlink_traps_fini
- mlxsw_sp_trap_init
- mlxsw_sp_trap_fini
- mlxsw_sp_trap_action_set
- mlxsw_sp_trap_group_policer_init
- __mlxsw_sp_trap_group_init
- mlxsw_sp_trap_group_init
1
2
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
46
47
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;
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 }