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