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 int mlxsw_sp_rx_listener(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
29 				u8 local_port,
30 				struct mlxsw_sp_port *mlxsw_sp_port)
31 {
32 	struct mlxsw_sp_port_pcpu_stats *pcpu_stats;
33 
34 	if (unlikely(!mlxsw_sp_port)) {
35 		dev_warn_ratelimited(mlxsw_sp->bus_info->dev, "Port %d: skb received for non-existent port\n",
36 				     local_port);
37 		kfree_skb(skb);
38 		return -EINVAL;
39 	}
40 
41 	skb->dev = mlxsw_sp_port->dev;
42 
43 	pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats);
44 	u64_stats_update_begin(&pcpu_stats->syncp);
45 	pcpu_stats->rx_packets++;
46 	pcpu_stats->rx_bytes += skb->len;
47 	u64_stats_update_end(&pcpu_stats->syncp);
48 
49 	skb->protocol = eth_type_trans(skb, skb->dev);
50 
51 	return 0;
52 }
53 
54 static void mlxsw_sp_rx_drop_listener(struct sk_buff *skb, u8 local_port,
55 				      void *trap_ctx)
56 {
57 	struct devlink_port *in_devlink_port;
58 	struct mlxsw_sp_port *mlxsw_sp_port;
59 	struct mlxsw_sp *mlxsw_sp;
60 	struct devlink *devlink;
61 	int err;
62 
63 	mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
64 	mlxsw_sp_port = mlxsw_sp->ports[local_port];
65 
66 	err = mlxsw_sp_rx_listener(mlxsw_sp, skb, local_port, mlxsw_sp_port);
67 	if (err)
68 		return;
69 
70 	devlink = priv_to_devlink(mlxsw_sp->core);
71 	in_devlink_port = mlxsw_core_port_devlink_port_get(mlxsw_sp->core,
72 							   local_port);
73 	skb_push(skb, ETH_HLEN);
74 	devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port, NULL);
75 	consume_skb(skb);
76 }
77 
78 static void mlxsw_sp_rx_acl_drop_listener(struct sk_buff *skb, u8 local_port,
79 					  void *trap_ctx)
80 {
81 	u32 cookie_index = mlxsw_skb_cb(skb)->cookie_index;
82 	const struct flow_action_cookie *fa_cookie;
83 	struct devlink_port *in_devlink_port;
84 	struct mlxsw_sp_port *mlxsw_sp_port;
85 	struct mlxsw_sp *mlxsw_sp;
86 	struct devlink *devlink;
87 	int err;
88 
89 	mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
90 	mlxsw_sp_port = mlxsw_sp->ports[local_port];
91 
92 	err = mlxsw_sp_rx_listener(mlxsw_sp, skb, local_port, mlxsw_sp_port);
93 	if (err)
94 		return;
95 
96 	devlink = priv_to_devlink(mlxsw_sp->core);
97 	in_devlink_port = mlxsw_core_port_devlink_port_get(mlxsw_sp->core,
98 							   local_port);
99 	skb_push(skb, ETH_HLEN);
100 	rcu_read_lock();
101 	fa_cookie = mlxsw_sp_acl_act_cookie_lookup(mlxsw_sp, cookie_index);
102 	devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port, fa_cookie);
103 	rcu_read_unlock();
104 	consume_skb(skb);
105 }
106 
107 static void mlxsw_sp_rx_exception_listener(struct sk_buff *skb, u8 local_port,
108 					   void *trap_ctx)
109 {
110 	struct devlink_port *in_devlink_port;
111 	struct mlxsw_sp_port *mlxsw_sp_port;
112 	struct mlxsw_sp *mlxsw_sp;
113 	struct devlink *devlink;
114 	int err;
115 
116 	mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
117 	mlxsw_sp_port = mlxsw_sp->ports[local_port];
118 
119 	err = mlxsw_sp_rx_listener(mlxsw_sp, skb, local_port, mlxsw_sp_port);
120 	if (err)
121 		return;
122 
123 	devlink = priv_to_devlink(mlxsw_sp->core);
124 	in_devlink_port = mlxsw_core_port_devlink_port_get(mlxsw_sp->core,
125 							   local_port);
126 	skb_push(skb, ETH_HLEN);
127 	devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port, NULL);
128 	skb_pull(skb, ETH_HLEN);
129 	skb->offload_fwd_mark = 1;
130 	netif_receive_skb(skb);
131 }
132 
133 #define MLXSW_SP_TRAP_DROP(_id, _group_id)				      \
134 	DEVLINK_TRAP_GENERIC(DROP, DROP, _id,				      \
135 			     DEVLINK_TRAP_GROUP_GENERIC(_group_id),	      \
136 			     MLXSW_SP_TRAP_METADATA)
137 
138 #define MLXSW_SP_TRAP_DROP_EXT(_id, _group_id, _metadata)		      \
139 	DEVLINK_TRAP_GENERIC(DROP, DROP, _id,				      \
140 			     DEVLINK_TRAP_GROUP_GENERIC(_group_id),	      \
141 			     MLXSW_SP_TRAP_METADATA | (_metadata))
142 
143 #define MLXSW_SP_TRAP_DRIVER_DROP(_id, _group_id)			      \
144 	DEVLINK_TRAP_DRIVER(DROP, DROP, DEVLINK_MLXSW_TRAP_ID_##_id,	      \
145 			    DEVLINK_MLXSW_TRAP_NAME_##_id,		      \
146 			    DEVLINK_TRAP_GROUP_GENERIC(_group_id),	      \
147 			    MLXSW_SP_TRAP_METADATA)
148 
149 #define MLXSW_SP_TRAP_EXCEPTION(_id, _group_id)		      \
150 	DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id,			      \
151 			     DEVLINK_TRAP_GROUP_GENERIC(_group_id),	      \
152 			     MLXSW_SP_TRAP_METADATA)
153 
154 #define MLXSW_SP_RXL_DISCARD(_id, _group_id)				      \
155 	MLXSW_RXL_DIS(mlxsw_sp_rx_drop_listener, DISCARD_##_id,		      \
156 		      TRAP_EXCEPTION_TO_CPU, false, SP_##_group_id,	      \
157 		      SET_FW_DEFAULT, SP_##_group_id)
158 
159 #define MLXSW_SP_RXL_ACL_DISCARD(_id, _en_group_id, _dis_group_id)	      \
160 	MLXSW_RXL_DIS(mlxsw_sp_rx_acl_drop_listener, DISCARD_##_id,	      \
161 		      TRAP_EXCEPTION_TO_CPU, false, SP_##_en_group_id,	      \
162 		      SET_FW_DEFAULT, SP_##_dis_group_id)
163 
164 #define MLXSW_SP_RXL_EXCEPTION(_id, _group_id, _action)			      \
165 	MLXSW_RXL(mlxsw_sp_rx_exception_listener, _id,			      \
166 		   _action, false, SP_##_group_id, SET_FW_DEFAULT)
167 
168 static const struct devlink_trap mlxsw_sp_traps_arr[] = {
169 	MLXSW_SP_TRAP_DROP(SMAC_MC, L2_DROPS),
170 	MLXSW_SP_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS),
171 	MLXSW_SP_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS),
172 	MLXSW_SP_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS),
173 	MLXSW_SP_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS),
174 	MLXSW_SP_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS),
175 	MLXSW_SP_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS),
176 	MLXSW_SP_TRAP_DROP(NON_IP_PACKET, L3_DROPS),
177 	MLXSW_SP_TRAP_DROP(UC_DIP_MC_DMAC, L3_DROPS),
178 	MLXSW_SP_TRAP_DROP(DIP_LB, L3_DROPS),
179 	MLXSW_SP_TRAP_DROP(SIP_MC, L3_DROPS),
180 	MLXSW_SP_TRAP_DROP(SIP_LB, L3_DROPS),
181 	MLXSW_SP_TRAP_DROP(CORRUPTED_IP_HDR, L3_DROPS),
182 	MLXSW_SP_TRAP_DROP(IPV4_SIP_BC, L3_DROPS),
183 	MLXSW_SP_TRAP_DROP(IPV6_MC_DIP_RESERVED_SCOPE, L3_DROPS),
184 	MLXSW_SP_TRAP_DROP(IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE, L3_DROPS),
185 	MLXSW_SP_TRAP_EXCEPTION(MTU_ERROR, L3_DROPS),
186 	MLXSW_SP_TRAP_EXCEPTION(TTL_ERROR, L3_DROPS),
187 	MLXSW_SP_TRAP_EXCEPTION(RPF, L3_DROPS),
188 	MLXSW_SP_TRAP_EXCEPTION(REJECT_ROUTE, L3_DROPS),
189 	MLXSW_SP_TRAP_EXCEPTION(UNRESOLVED_NEIGH, L3_DROPS),
190 	MLXSW_SP_TRAP_EXCEPTION(IPV4_LPM_UNICAST_MISS, L3_DROPS),
191 	MLXSW_SP_TRAP_EXCEPTION(IPV6_LPM_UNICAST_MISS, L3_DROPS),
192 	MLXSW_SP_TRAP_DRIVER_DROP(IRIF_DISABLED, L3_DROPS),
193 	MLXSW_SP_TRAP_DRIVER_DROP(ERIF_DISABLED, L3_DROPS),
194 	MLXSW_SP_TRAP_DROP(NON_ROUTABLE, L3_DROPS),
195 	MLXSW_SP_TRAP_EXCEPTION(DECAP_ERROR, TUNNEL_DROPS),
196 	MLXSW_SP_TRAP_DROP(OVERLAY_SMAC_MC, TUNNEL_DROPS),
197 	MLXSW_SP_TRAP_DROP_EXT(INGRESS_FLOW_ACTION_DROP, ACL_DROPS,
198 			       DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE),
199 	MLXSW_SP_TRAP_DROP_EXT(EGRESS_FLOW_ACTION_DROP, ACL_DROPS,
200 			       DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE),
201 };
202 
203 static const struct mlxsw_listener mlxsw_sp_listeners_arr[] = {
204 	MLXSW_SP_RXL_DISCARD(ING_PACKET_SMAC_MC, L2_DISCARDS),
205 	MLXSW_SP_RXL_DISCARD(ING_SWITCH_VTAG_ALLOW, L2_DISCARDS),
206 	MLXSW_SP_RXL_DISCARD(ING_SWITCH_VLAN, L2_DISCARDS),
207 	MLXSW_SP_RXL_DISCARD(ING_SWITCH_STP, L2_DISCARDS),
208 	MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_UC, L2_DISCARDS),
209 	MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_MC_NULL, L2_DISCARDS),
210 	MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_LB, L2_DISCARDS),
211 	MLXSW_SP_RXL_DISCARD(ROUTER2, L3_DISCARDS),
212 	MLXSW_SP_RXL_DISCARD(ING_ROUTER_NON_IP_PACKET, L3_DISCARDS),
213 	MLXSW_SP_RXL_DISCARD(ING_ROUTER_UC_DIP_MC_DMAC, L3_DISCARDS),
214 	MLXSW_SP_RXL_DISCARD(ING_ROUTER_DIP_LB, L3_DISCARDS),
215 	MLXSW_SP_RXL_DISCARD(ING_ROUTER_SIP_MC, L3_DISCARDS),
216 	MLXSW_SP_RXL_DISCARD(ING_ROUTER_SIP_LB, L3_DISCARDS),
217 	MLXSW_SP_RXL_DISCARD(ING_ROUTER_CORRUPTED_IP_HDR, L3_DISCARDS),
218 	MLXSW_SP_RXL_DISCARD(ING_ROUTER_IPV4_SIP_BC, L3_DISCARDS),
219 	MLXSW_SP_RXL_DISCARD(IPV6_MC_DIP_RESERVED_SCOPE, L3_DISCARDS),
220 	MLXSW_SP_RXL_DISCARD(IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE, L3_DISCARDS),
221 	MLXSW_SP_RXL_EXCEPTION(MTUERROR, ROUTER_EXP, TRAP_TO_CPU),
222 	MLXSW_SP_RXL_EXCEPTION(TTLERROR, ROUTER_EXP, TRAP_TO_CPU),
223 	MLXSW_SP_RXL_EXCEPTION(RPF, RPF, TRAP_TO_CPU),
224 	MLXSW_SP_RXL_EXCEPTION(RTR_INGRESS1, REMOTE_ROUTE, TRAP_TO_CPU),
225 	MLXSW_SP_RXL_EXCEPTION(HOST_MISS_IPV4, HOST_MISS, TRAP_TO_CPU),
226 	MLXSW_SP_RXL_EXCEPTION(HOST_MISS_IPV6, HOST_MISS, TRAP_TO_CPU),
227 	MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER3, REMOTE_ROUTE,
228 			       TRAP_EXCEPTION_TO_CPU),
229 	MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER_LPM4, ROUTER_EXP,
230 			       TRAP_EXCEPTION_TO_CPU),
231 	MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER_LPM6, ROUTER_EXP,
232 			       TRAP_EXCEPTION_TO_CPU),
233 	MLXSW_SP_RXL_DISCARD(ROUTER_IRIF_EN, L3_DISCARDS),
234 	MLXSW_SP_RXL_DISCARD(ROUTER_ERIF_EN, L3_DISCARDS),
235 	MLXSW_SP_RXL_DISCARD(NON_ROUTABLE, L3_DISCARDS),
236 	MLXSW_SP_RXL_EXCEPTION(DECAP_ECN0, ROUTER_EXP, TRAP_EXCEPTION_TO_CPU),
237 	MLXSW_SP_RXL_EXCEPTION(IPIP_DECAP_ERROR, ROUTER_EXP,
238 			       TRAP_EXCEPTION_TO_CPU),
239 	MLXSW_SP_RXL_EXCEPTION(DISCARD_DEC_PKT, TUNNEL_DISCARDS,
240 			       TRAP_EXCEPTION_TO_CPU),
241 	MLXSW_SP_RXL_DISCARD(OVERLAY_SMAC_MC, TUNNEL_DISCARDS),
242 	MLXSW_SP_RXL_ACL_DISCARD(INGRESS_ACL, ACL_DISCARDS, DUMMY),
243 	MLXSW_SP_RXL_ACL_DISCARD(EGRESS_ACL, ACL_DISCARDS, DUMMY),
244 };
245 
246 /* Mapping between hardware trap and devlink trap. Multiple hardware traps can
247  * be mapped to the same devlink trap. Order is according to
248  * 'mlxsw_sp_listeners_arr'.
249  */
250 static const u16 mlxsw_sp_listener_devlink_map[] = {
251 	DEVLINK_TRAP_GENERIC_ID_SMAC_MC,
252 	DEVLINK_TRAP_GENERIC_ID_VLAN_TAG_MISMATCH,
253 	DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER,
254 	DEVLINK_TRAP_GENERIC_ID_INGRESS_STP_FILTER,
255 	DEVLINK_TRAP_GENERIC_ID_EMPTY_TX_LIST,
256 	DEVLINK_TRAP_GENERIC_ID_EMPTY_TX_LIST,
257 	DEVLINK_TRAP_GENERIC_ID_PORT_LOOPBACK_FILTER,
258 	DEVLINK_TRAP_GENERIC_ID_BLACKHOLE_ROUTE,
259 	DEVLINK_TRAP_GENERIC_ID_NON_IP_PACKET,
260 	DEVLINK_TRAP_GENERIC_ID_UC_DIP_MC_DMAC,
261 	DEVLINK_TRAP_GENERIC_ID_DIP_LB,
262 	DEVLINK_TRAP_GENERIC_ID_SIP_MC,
263 	DEVLINK_TRAP_GENERIC_ID_SIP_LB,
264 	DEVLINK_TRAP_GENERIC_ID_CORRUPTED_IP_HDR,
265 	DEVLINK_TRAP_GENERIC_ID_IPV4_SIP_BC,
266 	DEVLINK_TRAP_GENERIC_ID_IPV6_MC_DIP_RESERVED_SCOPE,
267 	DEVLINK_TRAP_GENERIC_ID_IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE,
268 	DEVLINK_TRAP_GENERIC_ID_MTU_ERROR,
269 	DEVLINK_TRAP_GENERIC_ID_TTL_ERROR,
270 	DEVLINK_TRAP_GENERIC_ID_RPF,
271 	DEVLINK_TRAP_GENERIC_ID_REJECT_ROUTE,
272 	DEVLINK_TRAP_GENERIC_ID_UNRESOLVED_NEIGH,
273 	DEVLINK_TRAP_GENERIC_ID_UNRESOLVED_NEIGH,
274 	DEVLINK_TRAP_GENERIC_ID_UNRESOLVED_NEIGH,
275 	DEVLINK_TRAP_GENERIC_ID_IPV4_LPM_UNICAST_MISS,
276 	DEVLINK_TRAP_GENERIC_ID_IPV6_LPM_UNICAST_MISS,
277 	DEVLINK_MLXSW_TRAP_ID_IRIF_DISABLED,
278 	DEVLINK_MLXSW_TRAP_ID_ERIF_DISABLED,
279 	DEVLINK_TRAP_GENERIC_ID_NON_ROUTABLE,
280 	DEVLINK_TRAP_GENERIC_ID_DECAP_ERROR,
281 	DEVLINK_TRAP_GENERIC_ID_DECAP_ERROR,
282 	DEVLINK_TRAP_GENERIC_ID_DECAP_ERROR,
283 	DEVLINK_TRAP_GENERIC_ID_OVERLAY_SMAC_MC,
284 	DEVLINK_TRAP_GENERIC_ID_INGRESS_FLOW_ACTION_DROP,
285 	DEVLINK_TRAP_GENERIC_ID_EGRESS_FLOW_ACTION_DROP,
286 };
287 
288 #define MLXSW_SP_DISCARD_POLICER_ID	(MLXSW_REG_HTGT_TRAP_GROUP_MAX + 1)
289 #define MLXSW_SP_THIN_POLICER_ID	(MLXSW_SP_DISCARD_POLICER_ID + 1)
290 
291 static int mlxsw_sp_trap_cpu_policers_set(struct mlxsw_sp *mlxsw_sp)
292 {
293 	char qpcr_pl[MLXSW_REG_QPCR_LEN];
294 	int err;
295 
296 	mlxsw_reg_qpcr_pack(qpcr_pl, MLXSW_SP_DISCARD_POLICER_ID,
297 			    MLXSW_REG_QPCR_IR_UNITS_M, false, 10 * 1024, 7);
298 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
299 	if (err)
300 		return err;
301 
302 	/* The purpose of "thin" policer is to drop as many packets
303 	 * as possible. The dummy group is using it.
304 	 */
305 	mlxsw_reg_qpcr_pack(qpcr_pl, MLXSW_SP_THIN_POLICER_ID,
306 			    MLXSW_REG_QPCR_IR_UNITS_M, false, 1, 4);
307 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
308 }
309 
310 static int mlxsw_sp_trap_dummy_group_init(struct mlxsw_sp *mlxsw_sp)
311 {
312 	char htgt_pl[MLXSW_REG_HTGT_LEN];
313 
314 	mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_SP_DUMMY,
315 			    MLXSW_SP_THIN_POLICER_ID, 0, 1);
316 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl);
317 }
318 
319 int mlxsw_sp_devlink_traps_init(struct mlxsw_sp *mlxsw_sp)
320 {
321 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
322 	int err;
323 
324 	err = mlxsw_sp_trap_cpu_policers_set(mlxsw_sp);
325 	if (err)
326 		return err;
327 
328 	err = mlxsw_sp_trap_dummy_group_init(mlxsw_sp);
329 	if (err)
330 		return err;
331 
332 	if (WARN_ON(ARRAY_SIZE(mlxsw_sp_listener_devlink_map) !=
333 		    ARRAY_SIZE(mlxsw_sp_listeners_arr)))
334 		return -EINVAL;
335 
336 	return devlink_traps_register(devlink, mlxsw_sp_traps_arr,
337 				      ARRAY_SIZE(mlxsw_sp_traps_arr),
338 				      mlxsw_sp);
339 }
340 
341 void mlxsw_sp_devlink_traps_fini(struct mlxsw_sp *mlxsw_sp)
342 {
343 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
344 
345 	devlink_traps_unregister(devlink, mlxsw_sp_traps_arr,
346 				 ARRAY_SIZE(mlxsw_sp_traps_arr));
347 }
348 
349 int mlxsw_sp_trap_init(struct mlxsw_core *mlxsw_core,
350 		       const struct devlink_trap *trap, void *trap_ctx)
351 {
352 	int i;
353 
354 	for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) {
355 		const struct mlxsw_listener *listener;
356 		int err;
357 
358 		if (mlxsw_sp_listener_devlink_map[i] != trap->id)
359 			continue;
360 		listener = &mlxsw_sp_listeners_arr[i];
361 
362 		err = mlxsw_core_trap_register(mlxsw_core, listener, trap_ctx);
363 		if (err)
364 			return err;
365 	}
366 
367 	return 0;
368 }
369 
370 void mlxsw_sp_trap_fini(struct mlxsw_core *mlxsw_core,
371 			const struct devlink_trap *trap, void *trap_ctx)
372 {
373 	int i;
374 
375 	for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) {
376 		const struct mlxsw_listener *listener;
377 
378 		if (mlxsw_sp_listener_devlink_map[i] != trap->id)
379 			continue;
380 		listener = &mlxsw_sp_listeners_arr[i];
381 
382 		mlxsw_core_trap_unregister(mlxsw_core, listener, trap_ctx);
383 	}
384 }
385 
386 int mlxsw_sp_trap_action_set(struct mlxsw_core *mlxsw_core,
387 			     const struct devlink_trap *trap,
388 			     enum devlink_trap_action action)
389 {
390 	int i;
391 
392 	for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) {
393 		const struct mlxsw_listener *listener;
394 		bool enabled;
395 		int err;
396 
397 		if (mlxsw_sp_listener_devlink_map[i] != trap->id)
398 			continue;
399 		listener = &mlxsw_sp_listeners_arr[i];
400 		switch (action) {
401 		case DEVLINK_TRAP_ACTION_DROP:
402 			enabled = false;
403 			break;
404 		case DEVLINK_TRAP_ACTION_TRAP:
405 			enabled = true;
406 			break;
407 		default:
408 			return -EINVAL;
409 		}
410 		err = mlxsw_core_trap_state_set(mlxsw_core, listener, enabled);
411 		if (err)
412 			return err;
413 	}
414 
415 	return 0;
416 }
417 
418 int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
419 			     const struct devlink_trap_group *group)
420 {
421 	char htgt_pl[MLXSW_REG_HTGT_LEN];
422 	u8 priority, tc, group_id;
423 	u16 policer_id;
424 
425 	switch (group->id) {
426 	case DEVLINK_TRAP_GROUP_GENERIC_ID_L2_DROPS:
427 		group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L2_DISCARDS;
428 		policer_id = MLXSW_SP_DISCARD_POLICER_ID;
429 		priority = 0;
430 		tc = 1;
431 		break;
432 	case DEVLINK_TRAP_GROUP_GENERIC_ID_L3_DROPS:
433 		group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L3_DISCARDS;
434 		policer_id = MLXSW_SP_DISCARD_POLICER_ID;
435 		priority = 0;
436 		tc = 1;
437 		break;
438 	case DEVLINK_TRAP_GROUP_GENERIC_ID_TUNNEL_DROPS:
439 		group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_TUNNEL_DISCARDS;
440 		policer_id = MLXSW_SP_DISCARD_POLICER_ID;
441 		priority = 0;
442 		tc = 1;
443 		break;
444 	case DEVLINK_TRAP_GROUP_GENERIC_ID_ACL_DROPS:
445 		group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_ACL_DISCARDS;
446 		policer_id = MLXSW_SP_DISCARD_POLICER_ID;
447 		priority = 0;
448 		tc = 1;
449 		break;
450 	default:
451 		return -EINVAL;
452 	}
453 
454 	mlxsw_reg_htgt_pack(htgt_pl, group_id, policer_id, priority, tc);
455 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
456 }
457