1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2019 Mellanox Technologies. All rights reserved */
3 
4 #include <linux/bitops.h>
5 #include <linux/kernel.h>
6 #include <linux/netlink.h>
7 #include <net/devlink.h>
8 #include <uapi/linux/devlink.h>
9 
10 #include "core.h"
11 #include "reg.h"
12 #include "spectrum.h"
13 #include "spectrum_trap.h"
14 
15 struct mlxsw_sp_trap_policer_item {
16 	struct devlink_trap_policer policer;
17 	u16 hw_id;
18 };
19 
20 struct mlxsw_sp_trap_group_item {
21 	struct devlink_trap_group group;
22 	u16 hw_group_id;
23 	u8 priority;
24 };
25 
26 #define MLXSW_SP_TRAP_LISTENERS_MAX 3
27 
28 struct mlxsw_sp_trap_item {
29 	struct devlink_trap trap;
30 	struct mlxsw_listener listeners_arr[MLXSW_SP_TRAP_LISTENERS_MAX];
31 };
32 
33 /* All driver-specific traps must be documented in
34  * Documentation/networking/devlink/mlxsw.rst
35  */
36 enum {
37 	DEVLINK_MLXSW_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX,
38 	DEVLINK_MLXSW_TRAP_ID_IRIF_DISABLED,
39 	DEVLINK_MLXSW_TRAP_ID_ERIF_DISABLED,
40 };
41 
42 #define DEVLINK_MLXSW_TRAP_NAME_IRIF_DISABLED \
43 	"irif_disabled"
44 #define DEVLINK_MLXSW_TRAP_NAME_ERIF_DISABLED \
45 	"erif_disabled"
46 
47 #define MLXSW_SP_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT
48 
49 static int mlxsw_sp_rx_listener(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
50 				u8 local_port,
51 				struct mlxsw_sp_port *mlxsw_sp_port)
52 {
53 	struct mlxsw_sp_port_pcpu_stats *pcpu_stats;
54 
55 	if (unlikely(!mlxsw_sp_port)) {
56 		dev_warn_ratelimited(mlxsw_sp->bus_info->dev, "Port %d: skb received for non-existent port\n",
57 				     local_port);
58 		kfree_skb(skb);
59 		return -EINVAL;
60 	}
61 
62 	skb->dev = mlxsw_sp_port->dev;
63 
64 	pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats);
65 	u64_stats_update_begin(&pcpu_stats->syncp);
66 	pcpu_stats->rx_packets++;
67 	pcpu_stats->rx_bytes += skb->len;
68 	u64_stats_update_end(&pcpu_stats->syncp);
69 
70 	skb->protocol = eth_type_trans(skb, skb->dev);
71 
72 	return 0;
73 }
74 
75 static void mlxsw_sp_rx_drop_listener(struct sk_buff *skb, u8 local_port,
76 				      void *trap_ctx)
77 {
78 	struct devlink_port *in_devlink_port;
79 	struct mlxsw_sp_port *mlxsw_sp_port;
80 	struct mlxsw_sp *mlxsw_sp;
81 	struct devlink *devlink;
82 	int err;
83 
84 	mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
85 	mlxsw_sp_port = mlxsw_sp->ports[local_port];
86 
87 	err = mlxsw_sp_rx_listener(mlxsw_sp, skb, local_port, mlxsw_sp_port);
88 	if (err)
89 		return;
90 
91 	devlink = priv_to_devlink(mlxsw_sp->core);
92 	in_devlink_port = mlxsw_core_port_devlink_port_get(mlxsw_sp->core,
93 							   local_port);
94 	skb_push(skb, ETH_HLEN);
95 	devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port, NULL);
96 	consume_skb(skb);
97 }
98 
99 static void mlxsw_sp_rx_acl_drop_listener(struct sk_buff *skb, u8 local_port,
100 					  void *trap_ctx)
101 {
102 	u32 cookie_index = mlxsw_skb_cb(skb)->cookie_index;
103 	const struct flow_action_cookie *fa_cookie;
104 	struct devlink_port *in_devlink_port;
105 	struct mlxsw_sp_port *mlxsw_sp_port;
106 	struct mlxsw_sp *mlxsw_sp;
107 	struct devlink *devlink;
108 	int err;
109 
110 	mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
111 	mlxsw_sp_port = mlxsw_sp->ports[local_port];
112 
113 	err = mlxsw_sp_rx_listener(mlxsw_sp, skb, local_port, mlxsw_sp_port);
114 	if (err)
115 		return;
116 
117 	devlink = priv_to_devlink(mlxsw_sp->core);
118 	in_devlink_port = mlxsw_core_port_devlink_port_get(mlxsw_sp->core,
119 							   local_port);
120 	skb_push(skb, ETH_HLEN);
121 	rcu_read_lock();
122 	fa_cookie = mlxsw_sp_acl_act_cookie_lookup(mlxsw_sp, cookie_index);
123 	devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port, fa_cookie);
124 	rcu_read_unlock();
125 	consume_skb(skb);
126 }
127 
128 static int __mlxsw_sp_rx_no_mark_listener(struct sk_buff *skb, u8 local_port,
129 					  void *trap_ctx)
130 {
131 	struct devlink_port *in_devlink_port;
132 	struct mlxsw_sp_port *mlxsw_sp_port;
133 	struct mlxsw_sp *mlxsw_sp;
134 	struct devlink *devlink;
135 	int err;
136 
137 	mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
138 	mlxsw_sp_port = mlxsw_sp->ports[local_port];
139 
140 	err = mlxsw_sp_rx_listener(mlxsw_sp, skb, local_port, mlxsw_sp_port);
141 	if (err)
142 		return err;
143 
144 	devlink = priv_to_devlink(mlxsw_sp->core);
145 	in_devlink_port = mlxsw_core_port_devlink_port_get(mlxsw_sp->core,
146 							   local_port);
147 	skb_push(skb, ETH_HLEN);
148 	devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port, NULL);
149 	skb_pull(skb, ETH_HLEN);
150 
151 	return 0;
152 }
153 
154 static void mlxsw_sp_rx_no_mark_listener(struct sk_buff *skb, u8 local_port,
155 					 void *trap_ctx)
156 {
157 	int err;
158 
159 	err = __mlxsw_sp_rx_no_mark_listener(skb, local_port, trap_ctx);
160 	if (err)
161 		return;
162 
163 	netif_receive_skb(skb);
164 }
165 
166 static void mlxsw_sp_rx_mark_listener(struct sk_buff *skb, u8 local_port,
167 				      void *trap_ctx)
168 {
169 	skb->offload_fwd_mark = 1;
170 	mlxsw_sp_rx_no_mark_listener(skb, local_port, trap_ctx);
171 }
172 
173 static void mlxsw_sp_rx_l3_mark_listener(struct sk_buff *skb, u8 local_port,
174 					 void *trap_ctx)
175 {
176 	skb->offload_l3_fwd_mark = 1;
177 	skb->offload_fwd_mark = 1;
178 	mlxsw_sp_rx_no_mark_listener(skb, local_port, trap_ctx);
179 }
180 
181 static void mlxsw_sp_rx_ptp_listener(struct sk_buff *skb, u8 local_port,
182 				     void *trap_ctx)
183 {
184 	struct mlxsw_sp *mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
185 	int err;
186 
187 	err = __mlxsw_sp_rx_no_mark_listener(skb, local_port, trap_ctx);
188 	if (err)
189 		return;
190 
191 	/* The PTP handler expects skb->data to point to the start of the
192 	 * Ethernet header.
193 	 */
194 	skb_push(skb, ETH_HLEN);
195 	mlxsw_sp_ptp_receive(mlxsw_sp, skb, local_port);
196 }
197 
198 static void mlxsw_sp_rx_sample_listener(struct sk_buff *skb, u8 local_port,
199 					void *trap_ctx)
200 {
201 	struct mlxsw_sp *mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
202 	int err;
203 
204 	err = __mlxsw_sp_rx_no_mark_listener(skb, local_port, trap_ctx);
205 	if (err)
206 		return;
207 
208 	/* The sample handler expects skb->data to point to the start of the
209 	 * Ethernet header.
210 	 */
211 	skb_push(skb, ETH_HLEN);
212 	mlxsw_sp_sample_receive(mlxsw_sp, skb, local_port);
213 }
214 
215 #define MLXSW_SP_TRAP_DROP(_id, _group_id)				      \
216 	DEVLINK_TRAP_GENERIC(DROP, DROP, _id,				      \
217 			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
218 			     MLXSW_SP_TRAP_METADATA)
219 
220 #define MLXSW_SP_TRAP_DROP_EXT(_id, _group_id, _metadata)		      \
221 	DEVLINK_TRAP_GENERIC(DROP, DROP, _id,				      \
222 			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
223 			     MLXSW_SP_TRAP_METADATA | (_metadata))
224 
225 #define MLXSW_SP_TRAP_DRIVER_DROP(_id, _group_id)			      \
226 	DEVLINK_TRAP_DRIVER(DROP, DROP, DEVLINK_MLXSW_TRAP_ID_##_id,	      \
227 			    DEVLINK_MLXSW_TRAP_NAME_##_id,		      \
228 			    DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
229 			    MLXSW_SP_TRAP_METADATA)
230 
231 #define MLXSW_SP_TRAP_EXCEPTION(_id, _group_id)		      \
232 	DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id,			      \
233 			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
234 			     MLXSW_SP_TRAP_METADATA)
235 
236 #define MLXSW_SP_TRAP_CONTROL(_id, _group_id, _action)			      \
237 	DEVLINK_TRAP_GENERIC(CONTROL, _action, _id,			      \
238 			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
239 			     MLXSW_SP_TRAP_METADATA)
240 
241 #define MLXSW_SP_RXL_DISCARD(_id, _group_id)				      \
242 	MLXSW_RXL_DIS(mlxsw_sp_rx_drop_listener, DISCARD_##_id,		      \
243 		      TRAP_EXCEPTION_TO_CPU, false, SP_##_group_id,	      \
244 		      SET_FW_DEFAULT, SP_##_group_id)
245 
246 #define MLXSW_SP_RXL_ACL_DISCARD(_id, _en_group_id, _dis_group_id)	      \
247 	MLXSW_RXL_DIS(mlxsw_sp_rx_acl_drop_listener, DISCARD_##_id,	      \
248 		      TRAP_EXCEPTION_TO_CPU, false, SP_##_en_group_id,	      \
249 		      SET_FW_DEFAULT, SP_##_dis_group_id)
250 
251 #define MLXSW_SP_RXL_EXCEPTION(_id, _group_id, _action)			      \
252 	MLXSW_RXL(mlxsw_sp_rx_mark_listener, _id,			      \
253 		   _action, false, SP_##_group_id, SET_FW_DEFAULT)
254 
255 #define MLXSW_SP_RXL_NO_MARK(_id, _group_id, _action, _is_ctrl)		      \
256 	MLXSW_RXL(mlxsw_sp_rx_no_mark_listener, _id, _action,		      \
257 		  _is_ctrl, SP_##_group_id, DISCARD)
258 
259 #define MLXSW_SP_RXL_MARK(_id, _group_id, _action, _is_ctrl)		      \
260 	MLXSW_RXL(mlxsw_sp_rx_mark_listener, _id, _action, _is_ctrl,	      \
261 		  SP_##_group_id, DISCARD)
262 
263 #define MLXSW_SP_RXL_L3_MARK(_id, _group_id, _action, _is_ctrl)		      \
264 	MLXSW_RXL(mlxsw_sp_rx_l3_mark_listener, _id, _action, _is_ctrl,	      \
265 		  SP_##_group_id, DISCARD)
266 
267 #define MLXSW_SP_TRAP_POLICER(_id, _rate, _burst)			      \
268 	DEVLINK_TRAP_POLICER(_id, _rate, _burst,			      \
269 			     MLXSW_REG_QPCR_HIGHEST_CIR,		      \
270 			     MLXSW_REG_QPCR_LOWEST_CIR,			      \
271 			     1 << MLXSW_REG_QPCR_HIGHEST_CBS,		      \
272 			     1 << MLXSW_REG_QPCR_LOWEST_CBS)
273 
274 /* Ordered by policer identifier */
275 static const struct mlxsw_sp_trap_policer_item
276 mlxsw_sp_trap_policer_items_arr[] = {
277 	{
278 		.policer = MLXSW_SP_TRAP_POLICER(1, 10 * 1024, 128),
279 	},
280 	{
281 		.policer = MLXSW_SP_TRAP_POLICER(2, 128, 128),
282 	},
283 	{
284 		.policer = MLXSW_SP_TRAP_POLICER(3, 128, 128),
285 	},
286 	{
287 		.policer = MLXSW_SP_TRAP_POLICER(4, 128, 128),
288 	},
289 	{
290 		.policer = MLXSW_SP_TRAP_POLICER(5, 16 * 1024, 128),
291 	},
292 	{
293 		.policer = MLXSW_SP_TRAP_POLICER(6, 128, 128),
294 	},
295 	{
296 		.policer = MLXSW_SP_TRAP_POLICER(7, 1024, 128),
297 	},
298 	{
299 		.policer = MLXSW_SP_TRAP_POLICER(8, 20 * 1024, 1024),
300 	},
301 	{
302 		.policer = MLXSW_SP_TRAP_POLICER(9, 128, 128),
303 	},
304 	{
305 		.policer = MLXSW_SP_TRAP_POLICER(10, 1024, 128),
306 	},
307 	{
308 		.policer = MLXSW_SP_TRAP_POLICER(11, 360, 128),
309 	},
310 	{
311 		.policer = MLXSW_SP_TRAP_POLICER(12, 128, 128),
312 	},
313 	{
314 		.policer = MLXSW_SP_TRAP_POLICER(13, 128, 128),
315 	},
316 	{
317 		.policer = MLXSW_SP_TRAP_POLICER(14, 1024, 128),
318 	},
319 	{
320 		.policer = MLXSW_SP_TRAP_POLICER(15, 1024, 128),
321 	},
322 	{
323 		.policer = MLXSW_SP_TRAP_POLICER(16, 24 * 1024, 4096),
324 	},
325 	{
326 		.policer = MLXSW_SP_TRAP_POLICER(17, 19 * 1024, 4096),
327 	},
328 	{
329 		.policer = MLXSW_SP_TRAP_POLICER(18, 1024, 128),
330 	},
331 };
332 
333 static const struct mlxsw_sp_trap_group_item mlxsw_sp_trap_group_items_arr[] = {
334 	{
335 		.group = DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 1),
336 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L2_DISCARDS,
337 		.priority = 0,
338 	},
339 	{
340 		.group = DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 1),
341 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L3_DISCARDS,
342 		.priority = 0,
343 	},
344 	{
345 		.group = DEVLINK_TRAP_GROUP_GENERIC(L3_EXCEPTIONS, 1),
346 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L3_EXCEPTIONS,
347 		.priority = 2,
348 	},
349 	{
350 		.group = DEVLINK_TRAP_GROUP_GENERIC(TUNNEL_DROPS, 1),
351 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_TUNNEL_DISCARDS,
352 		.priority = 0,
353 	},
354 	{
355 		.group = DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 1),
356 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_ACL_DISCARDS,
357 		.priority = 0,
358 	},
359 	{
360 		.group = DEVLINK_TRAP_GROUP_GENERIC(STP, 2),
361 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_STP,
362 		.priority = 5,
363 	},
364 	{
365 		.group = DEVLINK_TRAP_GROUP_GENERIC(LACP, 3),
366 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_LACP,
367 		.priority = 5,
368 	},
369 	{
370 		.group = DEVLINK_TRAP_GROUP_GENERIC(LLDP, 4),
371 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_LLDP,
372 		.priority = 5,
373 	},
374 	{
375 		.group = DEVLINK_TRAP_GROUP_GENERIC(MC_SNOOPING, 5),
376 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_MC_SNOOPING,
377 		.priority = 3,
378 	},
379 	{
380 		.group = DEVLINK_TRAP_GROUP_GENERIC(DHCP, 6),
381 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP,
382 		.priority = 2,
383 	},
384 	{
385 		.group = DEVLINK_TRAP_GROUP_GENERIC(NEIGH_DISCOVERY, 7),
386 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_NEIGH_DISCOVERY,
387 		.priority = 2,
388 	},
389 	{
390 		.group = DEVLINK_TRAP_GROUP_GENERIC(BFD, 8),
391 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_BFD,
392 		.priority = 5,
393 	},
394 	{
395 		.group = DEVLINK_TRAP_GROUP_GENERIC(OSPF, 9),
396 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF,
397 		.priority = 5,
398 	},
399 	{
400 		.group = DEVLINK_TRAP_GROUP_GENERIC(BGP, 10),
401 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP,
402 		.priority = 4,
403 	},
404 	{
405 		.group = DEVLINK_TRAP_GROUP_GENERIC(VRRP, 11),
406 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_VRRP,
407 		.priority = 5,
408 	},
409 	{
410 		.group = DEVLINK_TRAP_GROUP_GENERIC(PIM, 12),
411 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_PIM,
412 		.priority = 5,
413 	},
414 	{
415 		.group = DEVLINK_TRAP_GROUP_GENERIC(UC_LB, 13),
416 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR,
417 		.priority = 0,
418 	},
419 	{
420 		.group = DEVLINK_TRAP_GROUP_GENERIC(LOCAL_DELIVERY, 14),
421 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME,
422 		.priority = 2,
423 	},
424 	{
425 		.group = DEVLINK_TRAP_GROUP_GENERIC(IPV6, 15),
426 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6,
427 		.priority = 2,
428 	},
429 	{
430 		.group = DEVLINK_TRAP_GROUP_GENERIC(PTP_EVENT, 16),
431 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_PTP0,
432 		.priority = 5,
433 	},
434 	{
435 		.group = DEVLINK_TRAP_GROUP_GENERIC(PTP_GENERAL, 17),
436 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_PTP1,
437 		.priority = 2,
438 	},
439 	{
440 		.group = DEVLINK_TRAP_GROUP_GENERIC(ACL_SAMPLE, 0),
441 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_PKT_SAMPLE,
442 		.priority = 0,
443 	},
444 	{
445 		.group = DEVLINK_TRAP_GROUP_GENERIC(ACL_TRAP, 18),
446 		.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_FLOW_LOGGING,
447 		.priority = 4,
448 	},
449 };
450 
451 static const struct mlxsw_sp_trap_item mlxsw_sp_trap_items_arr[] = {
452 	{
453 		.trap = MLXSW_SP_TRAP_DROP(SMAC_MC, L2_DROPS),
454 		.listeners_arr = {
455 			MLXSW_SP_RXL_DISCARD(ING_PACKET_SMAC_MC, L2_DISCARDS),
456 		},
457 	},
458 	{
459 		.trap = MLXSW_SP_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS),
460 		.listeners_arr = {
461 			MLXSW_SP_RXL_DISCARD(ING_SWITCH_VTAG_ALLOW,
462 					     L2_DISCARDS),
463 		},
464 	},
465 	{
466 		.trap = MLXSW_SP_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS),
467 		.listeners_arr = {
468 			MLXSW_SP_RXL_DISCARD(ING_SWITCH_VLAN, L2_DISCARDS),
469 		},
470 	},
471 	{
472 		.trap = MLXSW_SP_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS),
473 		.listeners_arr = {
474 			MLXSW_SP_RXL_DISCARD(ING_SWITCH_STP, L2_DISCARDS),
475 		},
476 	},
477 	{
478 		.trap = MLXSW_SP_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS),
479 		.listeners_arr = {
480 			MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_UC, L2_DISCARDS),
481 			MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_MC_NULL, L2_DISCARDS),
482 		},
483 	},
484 	{
485 		.trap = MLXSW_SP_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS),
486 		.listeners_arr = {
487 			MLXSW_SP_RXL_DISCARD(LOOKUP_SWITCH_LB, L2_DISCARDS),
488 		},
489 	},
490 	{
491 		.trap = MLXSW_SP_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS),
492 		.listeners_arr = {
493 			MLXSW_SP_RXL_DISCARD(ROUTER2, L3_DISCARDS),
494 		},
495 	},
496 	{
497 		.trap = MLXSW_SP_TRAP_DROP(NON_IP_PACKET, L3_DROPS),
498 		.listeners_arr = {
499 			MLXSW_SP_RXL_DISCARD(ING_ROUTER_NON_IP_PACKET,
500 					     L3_DISCARDS),
501 		},
502 	},
503 	{
504 		.trap = MLXSW_SP_TRAP_DROP(UC_DIP_MC_DMAC, L3_DROPS),
505 		.listeners_arr = {
506 			MLXSW_SP_RXL_DISCARD(ING_ROUTER_UC_DIP_MC_DMAC,
507 					     L3_DISCARDS),
508 		},
509 	},
510 	{
511 		.trap = MLXSW_SP_TRAP_DROP(DIP_LB, L3_DROPS),
512 		.listeners_arr = {
513 			MLXSW_SP_RXL_DISCARD(ING_ROUTER_DIP_LB, L3_DISCARDS),
514 		},
515 	},
516 	{
517 		.trap = MLXSW_SP_TRAP_DROP(SIP_MC, L3_DROPS),
518 		.listeners_arr = {
519 			MLXSW_SP_RXL_DISCARD(ING_ROUTER_SIP_MC, L3_DISCARDS),
520 		},
521 	},
522 	{
523 		.trap = MLXSW_SP_TRAP_DROP(SIP_LB, L3_DROPS),
524 		.listeners_arr = {
525 			MLXSW_SP_RXL_DISCARD(ING_ROUTER_SIP_LB, L3_DISCARDS),
526 		},
527 	},
528 	{
529 		.trap = MLXSW_SP_TRAP_DROP(CORRUPTED_IP_HDR, L3_DROPS),
530 		.listeners_arr = {
531 			MLXSW_SP_RXL_DISCARD(ING_ROUTER_CORRUPTED_IP_HDR,
532 					     L3_DISCARDS),
533 		},
534 	},
535 	{
536 		.trap = MLXSW_SP_TRAP_DROP(IPV4_SIP_BC, L3_DROPS),
537 		.listeners_arr = {
538 			MLXSW_SP_RXL_DISCARD(ING_ROUTER_IPV4_SIP_BC,
539 					     L3_DISCARDS),
540 		},
541 	},
542 	{
543 		.trap = MLXSW_SP_TRAP_DROP(IPV6_MC_DIP_RESERVED_SCOPE,
544 					   L3_DROPS),
545 		.listeners_arr = {
546 			MLXSW_SP_RXL_DISCARD(IPV6_MC_DIP_RESERVED_SCOPE,
547 					     L3_DISCARDS),
548 		},
549 	},
550 	{
551 		.trap = MLXSW_SP_TRAP_DROP(IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE,
552 					   L3_DROPS),
553 		.listeners_arr = {
554 			MLXSW_SP_RXL_DISCARD(IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE,
555 					     L3_DISCARDS),
556 		},
557 	},
558 	{
559 		.trap = MLXSW_SP_TRAP_EXCEPTION(MTU_ERROR, L3_EXCEPTIONS),
560 		.listeners_arr = {
561 			MLXSW_SP_RXL_EXCEPTION(MTUERROR, L3_EXCEPTIONS,
562 					       TRAP_TO_CPU),
563 		},
564 	},
565 	{
566 		.trap = MLXSW_SP_TRAP_EXCEPTION(TTL_ERROR, L3_EXCEPTIONS),
567 		.listeners_arr = {
568 			MLXSW_SP_RXL_EXCEPTION(TTLERROR, L3_EXCEPTIONS,
569 					       TRAP_TO_CPU),
570 		},
571 	},
572 	{
573 		.trap = MLXSW_SP_TRAP_EXCEPTION(RPF, L3_EXCEPTIONS),
574 		.listeners_arr = {
575 			MLXSW_SP_RXL_EXCEPTION(RPF, L3_EXCEPTIONS, TRAP_TO_CPU),
576 		},
577 	},
578 	{
579 		.trap = MLXSW_SP_TRAP_EXCEPTION(REJECT_ROUTE, L3_EXCEPTIONS),
580 		.listeners_arr = {
581 			MLXSW_SP_RXL_EXCEPTION(RTR_INGRESS1, L3_EXCEPTIONS,
582 					       TRAP_TO_CPU),
583 		},
584 	},
585 	{
586 		.trap = MLXSW_SP_TRAP_EXCEPTION(UNRESOLVED_NEIGH,
587 						L3_EXCEPTIONS),
588 		.listeners_arr = {
589 			MLXSW_SP_RXL_EXCEPTION(HOST_MISS_IPV4, L3_EXCEPTIONS,
590 					       TRAP_TO_CPU),
591 			MLXSW_SP_RXL_EXCEPTION(HOST_MISS_IPV6, L3_EXCEPTIONS,
592 					       TRAP_TO_CPU),
593 			MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER3, L3_EXCEPTIONS,
594 					       TRAP_EXCEPTION_TO_CPU),
595 		},
596 	},
597 	{
598 		.trap = MLXSW_SP_TRAP_EXCEPTION(IPV4_LPM_UNICAST_MISS,
599 						L3_EXCEPTIONS),
600 		.listeners_arr = {
601 			MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER_LPM4,
602 					       L3_EXCEPTIONS,
603 					       TRAP_EXCEPTION_TO_CPU),
604 		},
605 	},
606 	{
607 		.trap = MLXSW_SP_TRAP_EXCEPTION(IPV6_LPM_UNICAST_MISS,
608 						L3_EXCEPTIONS),
609 		.listeners_arr = {
610 			MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER_LPM6,
611 					       L3_EXCEPTIONS,
612 					       TRAP_EXCEPTION_TO_CPU),
613 		},
614 	},
615 	{
616 		.trap = MLXSW_SP_TRAP_DRIVER_DROP(IRIF_DISABLED, L3_DROPS),
617 		.listeners_arr = {
618 			MLXSW_SP_RXL_DISCARD(ROUTER_IRIF_EN, L3_DISCARDS),
619 		},
620 	},
621 	{
622 		.trap = MLXSW_SP_TRAP_DRIVER_DROP(ERIF_DISABLED, L3_DROPS),
623 		.listeners_arr = {
624 			MLXSW_SP_RXL_DISCARD(ROUTER_ERIF_EN, L3_DISCARDS),
625 		},
626 	},
627 	{
628 		.trap = MLXSW_SP_TRAP_DROP(NON_ROUTABLE, L3_DROPS),
629 		.listeners_arr = {
630 			MLXSW_SP_RXL_DISCARD(NON_ROUTABLE, L3_DISCARDS),
631 		},
632 	},
633 	{
634 		.trap = MLXSW_SP_TRAP_EXCEPTION(DECAP_ERROR, TUNNEL_DROPS),
635 		.listeners_arr = {
636 			MLXSW_SP_RXL_EXCEPTION(DECAP_ECN0, TUNNEL_DISCARDS,
637 					       TRAP_EXCEPTION_TO_CPU),
638 			MLXSW_SP_RXL_EXCEPTION(IPIP_DECAP_ERROR,
639 					       TUNNEL_DISCARDS,
640 					       TRAP_EXCEPTION_TO_CPU),
641 			MLXSW_SP_RXL_EXCEPTION(DISCARD_DEC_PKT, TUNNEL_DISCARDS,
642 					       TRAP_EXCEPTION_TO_CPU),
643 		},
644 	},
645 	{
646 		.trap = MLXSW_SP_TRAP_DROP(OVERLAY_SMAC_MC, TUNNEL_DROPS),
647 		.listeners_arr = {
648 			MLXSW_SP_RXL_DISCARD(OVERLAY_SMAC_MC, TUNNEL_DISCARDS),
649 		},
650 	},
651 	{
652 		.trap = MLXSW_SP_TRAP_DROP_EXT(INGRESS_FLOW_ACTION_DROP,
653 					       ACL_DROPS,
654 					       DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE),
655 		.listeners_arr = {
656 			MLXSW_SP_RXL_ACL_DISCARD(INGRESS_ACL, ACL_DISCARDS,
657 						 DUMMY),
658 		},
659 	},
660 	{
661 		.trap = MLXSW_SP_TRAP_DROP_EXT(EGRESS_FLOW_ACTION_DROP,
662 					       ACL_DROPS,
663 					       DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE),
664 		.listeners_arr = {
665 			MLXSW_SP_RXL_ACL_DISCARD(EGRESS_ACL, ACL_DISCARDS,
666 						 DUMMY),
667 		},
668 	},
669 	{
670 		.trap = MLXSW_SP_TRAP_CONTROL(STP, STP, TRAP),
671 		.listeners_arr = {
672 			MLXSW_SP_RXL_NO_MARK(STP, STP, TRAP_TO_CPU, true),
673 		},
674 	},
675 	{
676 		.trap = MLXSW_SP_TRAP_CONTROL(LACP, LACP, TRAP),
677 		.listeners_arr = {
678 			MLXSW_SP_RXL_NO_MARK(LACP, LACP, TRAP_TO_CPU, true),
679 		},
680 	},
681 	{
682 		.trap = MLXSW_SP_TRAP_CONTROL(LLDP, LLDP, TRAP),
683 		.listeners_arr = {
684 			MLXSW_RXL(mlxsw_sp_rx_ptp_listener, LLDP, TRAP_TO_CPU,
685 				  false, SP_LLDP, DISCARD),
686 		},
687 	},
688 	{
689 		.trap = MLXSW_SP_TRAP_CONTROL(IGMP_QUERY, MC_SNOOPING, MIRROR),
690 		.listeners_arr = {
691 			MLXSW_SP_RXL_MARK(IGMP_QUERY, MC_SNOOPING,
692 					  MIRROR_TO_CPU, false),
693 		},
694 	},
695 	{
696 		.trap = MLXSW_SP_TRAP_CONTROL(IGMP_V1_REPORT, MC_SNOOPING,
697 					      TRAP),
698 		.listeners_arr = {
699 			MLXSW_SP_RXL_NO_MARK(IGMP_V1_REPORT, MC_SNOOPING,
700 					     TRAP_TO_CPU, false),
701 		},
702 	},
703 	{
704 		.trap = MLXSW_SP_TRAP_CONTROL(IGMP_V2_REPORT, MC_SNOOPING,
705 					      TRAP),
706 		.listeners_arr = {
707 			MLXSW_SP_RXL_NO_MARK(IGMP_V2_REPORT, MC_SNOOPING,
708 					     TRAP_TO_CPU, false),
709 		},
710 	},
711 	{
712 		.trap = MLXSW_SP_TRAP_CONTROL(IGMP_V3_REPORT, MC_SNOOPING,
713 					      TRAP),
714 		.listeners_arr = {
715 			MLXSW_SP_RXL_NO_MARK(IGMP_V3_REPORT, MC_SNOOPING,
716 					     TRAP_TO_CPU, false),
717 		},
718 	},
719 	{
720 		.trap = MLXSW_SP_TRAP_CONTROL(IGMP_V2_LEAVE, MC_SNOOPING,
721 					      TRAP),
722 		.listeners_arr = {
723 			MLXSW_SP_RXL_NO_MARK(IGMP_V2_LEAVE, MC_SNOOPING,
724 					     TRAP_TO_CPU, false),
725 		},
726 	},
727 	{
728 		.trap = MLXSW_SP_TRAP_CONTROL(MLD_QUERY, MC_SNOOPING, MIRROR),
729 		.listeners_arr = {
730 			MLXSW_SP_RXL_MARK(IPV6_MLDV12_LISTENER_QUERY,
731 					  MC_SNOOPING, MIRROR_TO_CPU, false),
732 		},
733 	},
734 	{
735 		.trap = MLXSW_SP_TRAP_CONTROL(MLD_V1_REPORT, MC_SNOOPING,
736 					      TRAP),
737 		.listeners_arr = {
738 			MLXSW_SP_RXL_NO_MARK(IPV6_MLDV1_LISTENER_REPORT,
739 					     MC_SNOOPING, TRAP_TO_CPU, false),
740 		},
741 	},
742 	{
743 		.trap = MLXSW_SP_TRAP_CONTROL(MLD_V2_REPORT, MC_SNOOPING,
744 					      TRAP),
745 		.listeners_arr = {
746 			MLXSW_SP_RXL_NO_MARK(IPV6_MLDV2_LISTENER_REPORT,
747 					     MC_SNOOPING, TRAP_TO_CPU, false),
748 		},
749 	},
750 	{
751 		.trap = MLXSW_SP_TRAP_CONTROL(MLD_V1_DONE, MC_SNOOPING,
752 					      TRAP),
753 		.listeners_arr = {
754 			MLXSW_SP_RXL_NO_MARK(IPV6_MLDV1_LISTENER_DONE,
755 					     MC_SNOOPING, TRAP_TO_CPU, false),
756 		},
757 	},
758 	{
759 		.trap = MLXSW_SP_TRAP_CONTROL(IPV4_DHCP, DHCP, TRAP),
760 		.listeners_arr = {
761 			MLXSW_SP_RXL_MARK(IPV4_DHCP, DHCP, TRAP_TO_CPU, false),
762 		},
763 	},
764 	{
765 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_DHCP, DHCP, TRAP),
766 		.listeners_arr = {
767 			MLXSW_SP_RXL_MARK(IPV6_DHCP, DHCP, TRAP_TO_CPU, false),
768 		},
769 	},
770 	{
771 		.trap = MLXSW_SP_TRAP_CONTROL(ARP_REQUEST, NEIGH_DISCOVERY,
772 					      MIRROR),
773 		.listeners_arr = {
774 			MLXSW_SP_RXL_MARK(ARPBC, NEIGH_DISCOVERY, MIRROR_TO_CPU,
775 					  false),
776 		},
777 	},
778 	{
779 		.trap = MLXSW_SP_TRAP_CONTROL(ARP_RESPONSE, NEIGH_DISCOVERY,
780 					      MIRROR),
781 		.listeners_arr = {
782 			MLXSW_SP_RXL_MARK(ARPUC, NEIGH_DISCOVERY, MIRROR_TO_CPU,
783 					  false),
784 		},
785 	},
786 	{
787 		.trap = MLXSW_SP_TRAP_CONTROL(ARP_OVERLAY, NEIGH_DISCOVERY,
788 					      TRAP),
789 		.listeners_arr = {
790 			MLXSW_SP_RXL_NO_MARK(NVE_DECAP_ARP, NEIGH_DISCOVERY,
791 					     TRAP_TO_CPU, false),
792 		},
793 	},
794 	{
795 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_NEIGH_SOLICIT,
796 					      NEIGH_DISCOVERY, TRAP),
797 		.listeners_arr = {
798 			MLXSW_SP_RXL_MARK(L3_IPV6_NEIGHBOR_SOLICITATION,
799 					  NEIGH_DISCOVERY, TRAP_TO_CPU, false),
800 		},
801 	},
802 	{
803 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_NEIGH_ADVERT,
804 					      NEIGH_DISCOVERY, TRAP),
805 		.listeners_arr = {
806 			MLXSW_SP_RXL_MARK(L3_IPV6_NEIGHBOR_ADVERTISEMENT,
807 					  NEIGH_DISCOVERY, TRAP_TO_CPU, false),
808 		},
809 	},
810 	{
811 		.trap = MLXSW_SP_TRAP_CONTROL(IPV4_BFD, BFD, TRAP),
812 		.listeners_arr = {
813 			MLXSW_SP_RXL_MARK(IPV4_BFD, BFD, TRAP_TO_CPU, false),
814 		},
815 	},
816 	{
817 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_BFD, BFD, TRAP),
818 		.listeners_arr = {
819 			MLXSW_SP_RXL_MARK(IPV6_BFD, BFD, TRAP_TO_CPU, false),
820 		},
821 	},
822 	{
823 		.trap = MLXSW_SP_TRAP_CONTROL(IPV4_OSPF, OSPF, TRAP),
824 		.listeners_arr = {
825 			MLXSW_SP_RXL_MARK(IPV4_OSPF, OSPF, TRAP_TO_CPU, false),
826 		},
827 	},
828 	{
829 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_OSPF, OSPF, TRAP),
830 		.listeners_arr = {
831 			MLXSW_SP_RXL_MARK(IPV6_OSPF, OSPF, TRAP_TO_CPU, false),
832 		},
833 	},
834 	{
835 		.trap = MLXSW_SP_TRAP_CONTROL(IPV4_BGP, BGP, TRAP),
836 		.listeners_arr = {
837 			MLXSW_SP_RXL_MARK(IPV4_BGP, BGP, TRAP_TO_CPU, false),
838 		},
839 	},
840 	{
841 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_BGP, BGP, TRAP),
842 		.listeners_arr = {
843 			MLXSW_SP_RXL_MARK(IPV6_BGP, BGP, TRAP_TO_CPU, false),
844 		},
845 	},
846 	{
847 		.trap = MLXSW_SP_TRAP_CONTROL(IPV4_VRRP, VRRP, TRAP),
848 		.listeners_arr = {
849 			MLXSW_SP_RXL_MARK(IPV4_VRRP, VRRP, TRAP_TO_CPU, false),
850 		},
851 	},
852 	{
853 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_VRRP, VRRP, TRAP),
854 		.listeners_arr = {
855 			MLXSW_SP_RXL_MARK(IPV6_VRRP, VRRP, TRAP_TO_CPU, false),
856 		},
857 	},
858 	{
859 		.trap = MLXSW_SP_TRAP_CONTROL(IPV4_PIM, PIM, TRAP),
860 		.listeners_arr = {
861 			MLXSW_SP_RXL_MARK(IPV4_PIM, PIM, TRAP_TO_CPU, false),
862 		},
863 	},
864 	{
865 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_PIM, PIM, TRAP),
866 		.listeners_arr = {
867 			MLXSW_SP_RXL_MARK(IPV6_PIM, PIM, TRAP_TO_CPU, false),
868 		},
869 	},
870 	{
871 		.trap = MLXSW_SP_TRAP_CONTROL(UC_LB, UC_LB, MIRROR),
872 		.listeners_arr = {
873 			MLXSW_SP_RXL_L3_MARK(LBERROR, LBERROR, MIRROR_TO_CPU,
874 					     false),
875 		},
876 	},
877 	{
878 		.trap = MLXSW_SP_TRAP_CONTROL(LOCAL_ROUTE, LOCAL_DELIVERY,
879 					      TRAP),
880 		.listeners_arr = {
881 			MLXSW_SP_RXL_MARK(IP2ME, IP2ME, TRAP_TO_CPU, false),
882 		},
883 	},
884 	{
885 		.trap = MLXSW_SP_TRAP_CONTROL(EXTERNAL_ROUTE, LOCAL_DELIVERY,
886 					      TRAP),
887 		.listeners_arr = {
888 			MLXSW_SP_RXL_MARK(RTR_INGRESS0, IP2ME, TRAP_TO_CPU,
889 					  false),
890 		},
891 	},
892 	{
893 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_UC_DIP_LINK_LOCAL_SCOPE,
894 					      LOCAL_DELIVERY, TRAP),
895 		.listeners_arr = {
896 			MLXSW_SP_RXL_MARK(IPV6_LINK_LOCAL_DEST, IP2ME,
897 					  TRAP_TO_CPU, false),
898 		},
899 	},
900 	{
901 		.trap = MLXSW_SP_TRAP_CONTROL(IPV4_ROUTER_ALERT, LOCAL_DELIVERY,
902 					      TRAP),
903 		.listeners_arr = {
904 			MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV4, IP2ME, TRAP_TO_CPU,
905 					  false),
906 		},
907 	},
908 	{
909 		/* IPV6_ROUTER_ALERT is defined in uAPI as 22, but it is not
910 		 * used in this file, so undefine it.
911 		 */
912 		#undef IPV6_ROUTER_ALERT
913 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_ROUTER_ALERT, LOCAL_DELIVERY,
914 					      TRAP),
915 		.listeners_arr = {
916 			MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV6, IP2ME, TRAP_TO_CPU,
917 					  false),
918 		},
919 	},
920 	{
921 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_DIP_ALL_NODES, IPV6, TRAP),
922 		.listeners_arr = {
923 			MLXSW_SP_RXL_MARK(IPV6_ALL_NODES_LINK, IPV6,
924 					  TRAP_TO_CPU, false),
925 		},
926 	},
927 	{
928 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_DIP_ALL_ROUTERS, IPV6, TRAP),
929 		.listeners_arr = {
930 			MLXSW_SP_RXL_MARK(IPV6_ALL_ROUTERS_LINK, IPV6,
931 					  TRAP_TO_CPU, false),
932 		},
933 	},
934 	{
935 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_ROUTER_SOLICIT, IPV6, TRAP),
936 		.listeners_arr = {
937 			MLXSW_SP_RXL_MARK(L3_IPV6_ROUTER_SOLICITATION, IPV6,
938 					  TRAP_TO_CPU, false),
939 		},
940 	},
941 	{
942 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_ROUTER_ADVERT, IPV6, TRAP),
943 		.listeners_arr = {
944 			MLXSW_SP_RXL_MARK(L3_IPV6_ROUTER_ADVERTISEMENT, IPV6,
945 					  TRAP_TO_CPU, false),
946 		},
947 	},
948 	{
949 		.trap = MLXSW_SP_TRAP_CONTROL(IPV6_REDIRECT, IPV6, TRAP),
950 		.listeners_arr = {
951 			MLXSW_SP_RXL_MARK(L3_IPV6_REDIRECTION, IPV6,
952 					  TRAP_TO_CPU, false),
953 		},
954 	},
955 	{
956 		.trap = MLXSW_SP_TRAP_CONTROL(PTP_EVENT, PTP_EVENT, TRAP),
957 		.listeners_arr = {
958 			MLXSW_RXL(mlxsw_sp_rx_ptp_listener, PTP0, TRAP_TO_CPU,
959 				  false, SP_PTP0, DISCARD),
960 		},
961 	},
962 	{
963 		.trap = MLXSW_SP_TRAP_CONTROL(PTP_GENERAL, PTP_GENERAL, TRAP),
964 		.listeners_arr = {
965 			MLXSW_SP_RXL_NO_MARK(PTP1, PTP1, TRAP_TO_CPU, false),
966 		},
967 	},
968 	{
969 		.trap = MLXSW_SP_TRAP_CONTROL(FLOW_ACTION_SAMPLE, ACL_SAMPLE,
970 					      MIRROR),
971 		.listeners_arr = {
972 			MLXSW_RXL(mlxsw_sp_rx_sample_listener, PKT_SAMPLE,
973 				  MIRROR_TO_CPU, false, SP_PKT_SAMPLE, DISCARD),
974 		},
975 	},
976 	{
977 		.trap = MLXSW_SP_TRAP_CONTROL(FLOW_ACTION_TRAP, ACL_TRAP, TRAP),
978 		.listeners_arr = {
979 			MLXSW_SP_RXL_NO_MARK(ACL0, FLOW_LOGGING, TRAP_TO_CPU,
980 					     false),
981 		},
982 	},
983 };
984 
985 static struct mlxsw_sp_trap_policer_item *
986 mlxsw_sp_trap_policer_item_lookup(struct mlxsw_sp *mlxsw_sp, u32 id)
987 {
988 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
989 	int i;
990 
991 	for (i = 0; i < trap->policers_count; i++) {
992 		if (trap->policer_items_arr[i].policer.id == id)
993 			return &trap->policer_items_arr[i];
994 	}
995 
996 	return NULL;
997 }
998 
999 static struct mlxsw_sp_trap_group_item *
1000 mlxsw_sp_trap_group_item_lookup(struct mlxsw_sp *mlxsw_sp, u16 id)
1001 {
1002 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
1003 	int i;
1004 
1005 	for (i = 0; i < trap->groups_count; i++) {
1006 		if (trap->group_items_arr[i].group.id == id)
1007 			return &trap->group_items_arr[i];
1008 	}
1009 
1010 	return NULL;
1011 }
1012 
1013 static struct mlxsw_sp_trap_item *
1014 mlxsw_sp_trap_item_lookup(struct mlxsw_sp *mlxsw_sp, u16 id)
1015 {
1016 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
1017 	int i;
1018 
1019 	for (i = 0; i < trap->traps_count; i++) {
1020 		if (trap->trap_items_arr[i].trap.id == id)
1021 			return &trap->trap_items_arr[i];
1022 	}
1023 
1024 	return NULL;
1025 }
1026 
1027 static int mlxsw_sp_trap_cpu_policers_set(struct mlxsw_sp *mlxsw_sp)
1028 {
1029 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
1030 	char qpcr_pl[MLXSW_REG_QPCR_LEN];
1031 	u16 hw_id;
1032 
1033 	/* The purpose of "thin" policer is to drop as many packets
1034 	 * as possible. The dummy group is using it.
1035 	 */
1036 	hw_id = find_first_zero_bit(trap->policers_usage, trap->max_policers);
1037 	if (WARN_ON(hw_id == trap->max_policers))
1038 		return -ENOBUFS;
1039 
1040 	__set_bit(hw_id, trap->policers_usage);
1041 	trap->thin_policer_hw_id = hw_id;
1042 	mlxsw_reg_qpcr_pack(qpcr_pl, hw_id, MLXSW_REG_QPCR_IR_UNITS_M,
1043 			    false, 1, 4);
1044 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
1045 }
1046 
1047 static int mlxsw_sp_trap_dummy_group_init(struct mlxsw_sp *mlxsw_sp)
1048 {
1049 	char htgt_pl[MLXSW_REG_HTGT_LEN];
1050 
1051 	mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_SP_DUMMY,
1052 			    mlxsw_sp->trap->thin_policer_hw_id, 0, 1);
1053 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl);
1054 }
1055 
1056 static int mlxsw_sp_trap_policer_items_arr_init(struct mlxsw_sp *mlxsw_sp)
1057 {
1058 	size_t elem_size = sizeof(struct mlxsw_sp_trap_policer_item);
1059 	u64 arr_size = ARRAY_SIZE(mlxsw_sp_trap_policer_items_arr);
1060 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
1061 	u64 free_policers = 0;
1062 	u32 last_id;
1063 	int i;
1064 
1065 	for_each_clear_bit(i, trap->policers_usage, trap->max_policers)
1066 		free_policers++;
1067 
1068 	if (arr_size > free_policers) {
1069 		dev_err(mlxsw_sp->bus_info->dev, "Exceeded number of supported packet trap policers\n");
1070 		return -ENOBUFS;
1071 	}
1072 
1073 	trap->policer_items_arr = kcalloc(free_policers, elem_size, GFP_KERNEL);
1074 	if (!trap->policer_items_arr)
1075 		return -ENOMEM;
1076 
1077 	trap->policers_count = free_policers;
1078 
1079 	/* Initialize policer items array with pre-defined policers. */
1080 	memcpy(trap->policer_items_arr, mlxsw_sp_trap_policer_items_arr,
1081 	       elem_size * arr_size);
1082 
1083 	/* Initialize policer items array with the rest of the available
1084 	 * policers.
1085 	 */
1086 	last_id = mlxsw_sp_trap_policer_items_arr[arr_size - 1].policer.id;
1087 	for (i = arr_size; i < trap->policers_count; i++) {
1088 		const struct mlxsw_sp_trap_policer_item *policer_item;
1089 
1090 		/* Use parameters set for first policer and override
1091 		 * relevant ones.
1092 		 */
1093 		policer_item = &mlxsw_sp_trap_policer_items_arr[0];
1094 		trap->policer_items_arr[i] = *policer_item;
1095 		trap->policer_items_arr[i].policer.id = ++last_id;
1096 		trap->policer_items_arr[i].policer.init_rate = 1;
1097 		trap->policer_items_arr[i].policer.init_burst = 16;
1098 	}
1099 
1100 	return 0;
1101 }
1102 
1103 static void mlxsw_sp_trap_policer_items_arr_fini(struct mlxsw_sp *mlxsw_sp)
1104 {
1105 	kfree(mlxsw_sp->trap->policer_items_arr);
1106 }
1107 
1108 static int mlxsw_sp_trap_policers_init(struct mlxsw_sp *mlxsw_sp)
1109 {
1110 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1111 	const struct mlxsw_sp_trap_policer_item *policer_item;
1112 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
1113 	int err, i;
1114 
1115 	err = mlxsw_sp_trap_policer_items_arr_init(mlxsw_sp);
1116 	if (err)
1117 		return err;
1118 
1119 	for (i = 0; i < trap->policers_count; i++) {
1120 		policer_item = &trap->policer_items_arr[i];
1121 		err = devlink_trap_policers_register(devlink,
1122 						     &policer_item->policer, 1);
1123 		if (err)
1124 			goto err_trap_policer_register;
1125 	}
1126 
1127 	return 0;
1128 
1129 err_trap_policer_register:
1130 	for (i--; i >= 0; i--) {
1131 		policer_item = &trap->policer_items_arr[i];
1132 		devlink_trap_policers_unregister(devlink,
1133 						 &policer_item->policer, 1);
1134 	}
1135 	mlxsw_sp_trap_policer_items_arr_fini(mlxsw_sp);
1136 	return err;
1137 }
1138 
1139 static void mlxsw_sp_trap_policers_fini(struct mlxsw_sp *mlxsw_sp)
1140 {
1141 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1142 	const struct mlxsw_sp_trap_policer_item *policer_item;
1143 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
1144 	int i;
1145 
1146 	for (i = trap->policers_count - 1; i >= 0; i--) {
1147 		policer_item = &trap->policer_items_arr[i];
1148 		devlink_trap_policers_unregister(devlink,
1149 						 &policer_item->policer, 1);
1150 	}
1151 	mlxsw_sp_trap_policer_items_arr_fini(mlxsw_sp);
1152 }
1153 
1154 static int mlxsw_sp_trap_groups_init(struct mlxsw_sp *mlxsw_sp)
1155 {
1156 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1157 	const struct mlxsw_sp_trap_group_item *group_item;
1158 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
1159 	int err, i;
1160 
1161 	trap->group_items_arr = kmemdup(mlxsw_sp_trap_group_items_arr,
1162 					sizeof(mlxsw_sp_trap_group_items_arr),
1163 					GFP_KERNEL);
1164 	if (!trap->group_items_arr)
1165 		return -ENOMEM;
1166 
1167 	trap->groups_count = ARRAY_SIZE(mlxsw_sp_trap_group_items_arr);
1168 
1169 	for (i = 0; i < trap->groups_count; i++) {
1170 		group_item = &trap->group_items_arr[i];
1171 		err = devlink_trap_groups_register(devlink, &group_item->group,
1172 						   1);
1173 		if (err)
1174 			goto err_trap_group_register;
1175 	}
1176 
1177 	return 0;
1178 
1179 err_trap_group_register:
1180 	for (i--; i >= 0; i--) {
1181 		group_item = &trap->group_items_arr[i];
1182 		devlink_trap_groups_unregister(devlink, &group_item->group, 1);
1183 	}
1184 	kfree(trap->group_items_arr);
1185 	return err;
1186 }
1187 
1188 static void mlxsw_sp_trap_groups_fini(struct mlxsw_sp *mlxsw_sp)
1189 {
1190 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1191 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
1192 	int i;
1193 
1194 	for (i = trap->groups_count - 1; i >= 0; i--) {
1195 		const struct mlxsw_sp_trap_group_item *group_item;
1196 
1197 		group_item = &trap->group_items_arr[i];
1198 		devlink_trap_groups_unregister(devlink, &group_item->group, 1);
1199 	}
1200 	kfree(trap->group_items_arr);
1201 }
1202 
1203 static bool
1204 mlxsw_sp_trap_listener_is_valid(const struct mlxsw_listener *listener)
1205 {
1206 	return listener->trap_id != 0;
1207 }
1208 
1209 static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp)
1210 {
1211 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1212 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
1213 	const struct mlxsw_sp_trap_item *trap_item;
1214 	int err, i;
1215 
1216 	trap->trap_items_arr = kmemdup(mlxsw_sp_trap_items_arr,
1217 				       sizeof(mlxsw_sp_trap_items_arr),
1218 				       GFP_KERNEL);
1219 	if (!trap->trap_items_arr)
1220 		return -ENOMEM;
1221 
1222 	trap->traps_count = ARRAY_SIZE(mlxsw_sp_trap_items_arr);
1223 
1224 	for (i = 0; i < trap->traps_count; i++) {
1225 		trap_item = &trap->trap_items_arr[i];
1226 		err = devlink_traps_register(devlink, &trap_item->trap, 1,
1227 					     mlxsw_sp);
1228 		if (err)
1229 			goto err_trap_register;
1230 	}
1231 
1232 	return 0;
1233 
1234 err_trap_register:
1235 	for (i--; i >= 0; i--) {
1236 		trap_item = &trap->trap_items_arr[i];
1237 		devlink_traps_unregister(devlink, &trap_item->trap, 1);
1238 	}
1239 	kfree(trap->trap_items_arr);
1240 	return err;
1241 }
1242 
1243 static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp)
1244 {
1245 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1246 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
1247 	int i;
1248 
1249 	for (i = trap->traps_count - 1; i >= 0; i--) {
1250 		const struct mlxsw_sp_trap_item *trap_item;
1251 
1252 		trap_item = &trap->trap_items_arr[i];
1253 		devlink_traps_unregister(devlink, &trap_item->trap, 1);
1254 	}
1255 	kfree(trap->trap_items_arr);
1256 }
1257 
1258 int mlxsw_sp_devlink_traps_init(struct mlxsw_sp *mlxsw_sp)
1259 {
1260 	int err;
1261 
1262 	err = mlxsw_sp_trap_cpu_policers_set(mlxsw_sp);
1263 	if (err)
1264 		return err;
1265 
1266 	err = mlxsw_sp_trap_dummy_group_init(mlxsw_sp);
1267 	if (err)
1268 		return err;
1269 
1270 	err = mlxsw_sp_trap_policers_init(mlxsw_sp);
1271 	if (err)
1272 		return err;
1273 
1274 	err = mlxsw_sp_trap_groups_init(mlxsw_sp);
1275 	if (err)
1276 		goto err_trap_groups_init;
1277 
1278 	err = mlxsw_sp_traps_init(mlxsw_sp);
1279 	if (err)
1280 		goto err_traps_init;
1281 
1282 	return 0;
1283 
1284 err_traps_init:
1285 	mlxsw_sp_trap_groups_fini(mlxsw_sp);
1286 err_trap_groups_init:
1287 	mlxsw_sp_trap_policers_fini(mlxsw_sp);
1288 	return err;
1289 }
1290 
1291 void mlxsw_sp_devlink_traps_fini(struct mlxsw_sp *mlxsw_sp)
1292 {
1293 	mlxsw_sp_traps_fini(mlxsw_sp);
1294 	mlxsw_sp_trap_groups_fini(mlxsw_sp);
1295 	mlxsw_sp_trap_policers_fini(mlxsw_sp);
1296 }
1297 
1298 int mlxsw_sp_trap_init(struct mlxsw_core *mlxsw_core,
1299 		       const struct devlink_trap *trap, void *trap_ctx)
1300 {
1301 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1302 	const struct mlxsw_sp_trap_item *trap_item;
1303 	int i;
1304 
1305 	trap_item = mlxsw_sp_trap_item_lookup(mlxsw_sp, trap->id);
1306 	if (WARN_ON(!trap_item))
1307 		return -EINVAL;
1308 
1309 	for (i = 0; i < MLXSW_SP_TRAP_LISTENERS_MAX; i++) {
1310 		const struct mlxsw_listener *listener;
1311 		int err;
1312 
1313 		listener = &trap_item->listeners_arr[i];
1314 		if (!mlxsw_sp_trap_listener_is_valid(listener))
1315 			continue;
1316 		err = mlxsw_core_trap_register(mlxsw_core, listener, trap_ctx);
1317 		if (err)
1318 			return err;
1319 	}
1320 
1321 	return 0;
1322 }
1323 
1324 void mlxsw_sp_trap_fini(struct mlxsw_core *mlxsw_core,
1325 			const struct devlink_trap *trap, void *trap_ctx)
1326 {
1327 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1328 	const struct mlxsw_sp_trap_item *trap_item;
1329 	int i;
1330 
1331 	trap_item = mlxsw_sp_trap_item_lookup(mlxsw_sp, trap->id);
1332 	if (WARN_ON(!trap_item))
1333 		return;
1334 
1335 	for (i = MLXSW_SP_TRAP_LISTENERS_MAX - 1; i >= 0; i--) {
1336 		const struct mlxsw_listener *listener;
1337 
1338 		listener = &trap_item->listeners_arr[i];
1339 		if (!mlxsw_sp_trap_listener_is_valid(listener))
1340 			continue;
1341 		mlxsw_core_trap_unregister(mlxsw_core, listener, trap_ctx);
1342 	}
1343 }
1344 
1345 int mlxsw_sp_trap_action_set(struct mlxsw_core *mlxsw_core,
1346 			     const struct devlink_trap *trap,
1347 			     enum devlink_trap_action action)
1348 {
1349 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1350 	const struct mlxsw_sp_trap_item *trap_item;
1351 	int i;
1352 
1353 	trap_item = mlxsw_sp_trap_item_lookup(mlxsw_sp, trap->id);
1354 	if (WARN_ON(!trap_item))
1355 		return -EINVAL;
1356 
1357 	for (i = 0; i < MLXSW_SP_TRAP_LISTENERS_MAX; i++) {
1358 		const struct mlxsw_listener *listener;
1359 		bool enabled;
1360 		int err;
1361 
1362 		listener = &trap_item->listeners_arr[i];
1363 		if (!mlxsw_sp_trap_listener_is_valid(listener))
1364 			continue;
1365 
1366 		switch (action) {
1367 		case DEVLINK_TRAP_ACTION_DROP:
1368 			enabled = false;
1369 			break;
1370 		case DEVLINK_TRAP_ACTION_TRAP:
1371 			enabled = true;
1372 			break;
1373 		default:
1374 			return -EINVAL;
1375 		}
1376 		err = mlxsw_core_trap_state_set(mlxsw_core, listener, enabled);
1377 		if (err)
1378 			return err;
1379 	}
1380 
1381 	return 0;
1382 }
1383 
1384 static int
1385 __mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
1386 			   const struct devlink_trap_group *group,
1387 			   u32 policer_id)
1388 {
1389 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1390 	u16 hw_policer_id = MLXSW_REG_HTGT_INVALID_POLICER;
1391 	const struct mlxsw_sp_trap_group_item *group_item;
1392 	char htgt_pl[MLXSW_REG_HTGT_LEN];
1393 
1394 	group_item = mlxsw_sp_trap_group_item_lookup(mlxsw_sp, group->id);
1395 	if (WARN_ON(!group_item))
1396 		return -EINVAL;
1397 
1398 	if (policer_id) {
1399 		struct mlxsw_sp_trap_policer_item *policer_item;
1400 
1401 		policer_item = mlxsw_sp_trap_policer_item_lookup(mlxsw_sp,
1402 								 policer_id);
1403 		if (WARN_ON(!policer_item))
1404 			return -EINVAL;
1405 		hw_policer_id = policer_item->hw_id;
1406 	}
1407 
1408 	mlxsw_reg_htgt_pack(htgt_pl, group_item->hw_group_id, hw_policer_id,
1409 			    group_item->priority, group_item->priority);
1410 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
1411 }
1412 
1413 int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
1414 			     const struct devlink_trap_group *group)
1415 {
1416 	return __mlxsw_sp_trap_group_init(mlxsw_core, group,
1417 					  group->init_policer_id);
1418 }
1419 
1420 int mlxsw_sp_trap_group_set(struct mlxsw_core *mlxsw_core,
1421 			    const struct devlink_trap_group *group,
1422 			    const struct devlink_trap_policer *policer)
1423 {
1424 	u32 policer_id = policer ? policer->id : 0;
1425 
1426 	return __mlxsw_sp_trap_group_init(mlxsw_core, group, policer_id);
1427 }
1428 
1429 static int
1430 mlxsw_sp_trap_policer_item_init(struct mlxsw_sp *mlxsw_sp,
1431 				struct mlxsw_sp_trap_policer_item *policer_item)
1432 {
1433 	struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
1434 	u16 hw_id;
1435 
1436 	/* We should be able to allocate a policer because the number of
1437 	 * policers we registered with devlink is in according with the number
1438 	 * of available policers.
1439 	 */
1440 	hw_id = find_first_zero_bit(trap->policers_usage, trap->max_policers);
1441 	if (WARN_ON(hw_id == trap->max_policers))
1442 		return -ENOBUFS;
1443 
1444 	__set_bit(hw_id, trap->policers_usage);
1445 	policer_item->hw_id = hw_id;
1446 
1447 	return 0;
1448 }
1449 
1450 static void
1451 mlxsw_sp_trap_policer_item_fini(struct mlxsw_sp *mlxsw_sp,
1452 				struct mlxsw_sp_trap_policer_item *policer_item)
1453 {
1454 	__clear_bit(policer_item->hw_id, mlxsw_sp->trap->policers_usage);
1455 }
1456 
1457 static int mlxsw_sp_trap_policer_bs(u64 burst, u8 *p_burst_size,
1458 				    struct netlink_ext_ack *extack)
1459 {
1460 	int bs = fls64(burst) - 1;
1461 
1462 	if (burst != (BIT_ULL(bs))) {
1463 		NL_SET_ERR_MSG_MOD(extack, "Policer burst size is not power of two");
1464 		return -EINVAL;
1465 	}
1466 
1467 	*p_burst_size = bs;
1468 
1469 	return 0;
1470 }
1471 
1472 static int __mlxsw_sp_trap_policer_set(struct mlxsw_sp *mlxsw_sp, u16 hw_id,
1473 				       u64 rate, u64 burst, bool clear_counter,
1474 				       struct netlink_ext_ack *extack)
1475 {
1476 	char qpcr_pl[MLXSW_REG_QPCR_LEN];
1477 	u8 burst_size;
1478 	int err;
1479 
1480 	err = mlxsw_sp_trap_policer_bs(burst, &burst_size, extack);
1481 	if (err)
1482 		return err;
1483 
1484 	mlxsw_reg_qpcr_pack(qpcr_pl, hw_id, MLXSW_REG_QPCR_IR_UNITS_M, false,
1485 			    rate, burst_size);
1486 	mlxsw_reg_qpcr_clear_counter_set(qpcr_pl, clear_counter);
1487 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
1488 }
1489 
1490 int mlxsw_sp_trap_policer_init(struct mlxsw_core *mlxsw_core,
1491 			       const struct devlink_trap_policer *policer)
1492 {
1493 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1494 	struct mlxsw_sp_trap_policer_item *policer_item;
1495 	int err;
1496 
1497 	policer_item = mlxsw_sp_trap_policer_item_lookup(mlxsw_sp, policer->id);
1498 	if (WARN_ON(!policer_item))
1499 		return -EINVAL;
1500 
1501 	err = mlxsw_sp_trap_policer_item_init(mlxsw_sp, policer_item);
1502 	if (err)
1503 		return err;
1504 
1505 	err = __mlxsw_sp_trap_policer_set(mlxsw_sp, policer_item->hw_id,
1506 					  policer->init_rate,
1507 					  policer->init_burst, true, NULL);
1508 	if (err)
1509 		goto err_trap_policer_set;
1510 
1511 	return 0;
1512 
1513 err_trap_policer_set:
1514 	mlxsw_sp_trap_policer_item_fini(mlxsw_sp, policer_item);
1515 	return err;
1516 }
1517 
1518 void mlxsw_sp_trap_policer_fini(struct mlxsw_core *mlxsw_core,
1519 				const struct devlink_trap_policer *policer)
1520 {
1521 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1522 	struct mlxsw_sp_trap_policer_item *policer_item;
1523 
1524 	policer_item = mlxsw_sp_trap_policer_item_lookup(mlxsw_sp, policer->id);
1525 	if (WARN_ON(!policer_item))
1526 		return;
1527 
1528 	mlxsw_sp_trap_policer_item_fini(mlxsw_sp, policer_item);
1529 }
1530 
1531 int mlxsw_sp_trap_policer_set(struct mlxsw_core *mlxsw_core,
1532 			      const struct devlink_trap_policer *policer,
1533 			      u64 rate, u64 burst,
1534 			      struct netlink_ext_ack *extack)
1535 {
1536 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1537 	struct mlxsw_sp_trap_policer_item *policer_item;
1538 
1539 	policer_item = mlxsw_sp_trap_policer_item_lookup(mlxsw_sp, policer->id);
1540 	if (WARN_ON(!policer_item))
1541 		return -EINVAL;
1542 
1543 	return __mlxsw_sp_trap_policer_set(mlxsw_sp, policer_item->hw_id,
1544 					   rate, burst, false, extack);
1545 }
1546 
1547 int
1548 mlxsw_sp_trap_policer_counter_get(struct mlxsw_core *mlxsw_core,
1549 				  const struct devlink_trap_policer *policer,
1550 				  u64 *p_drops)
1551 {
1552 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1553 	struct mlxsw_sp_trap_policer_item *policer_item;
1554 	char qpcr_pl[MLXSW_REG_QPCR_LEN];
1555 	int err;
1556 
1557 	policer_item = mlxsw_sp_trap_policer_item_lookup(mlxsw_sp, policer->id);
1558 	if (WARN_ON(!policer_item))
1559 		return -EINVAL;
1560 
1561 	mlxsw_reg_qpcr_pack(qpcr_pl, policer_item->hw_id,
1562 			    MLXSW_REG_QPCR_IR_UNITS_M, false, 0, 0);
1563 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
1564 	if (err)
1565 		return err;
1566 
1567 	*p_drops = mlxsw_reg_qpcr_violate_count_get(qpcr_pl);
1568 
1569 	return 0;
1570 }
1571