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 
mlxsw_sp_rx_listener(struct mlxsw_sp * mlxsw_sp,struct sk_buff * skb,u8 local_port,struct mlxsw_sp_port * mlxsw_sp_port)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 
mlxsw_sp_rx_drop_listener(struct sk_buff * skb,u8 local_port,void * trap_ctx)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 
mlxsw_sp_devlink_traps_init(struct mlxsw_sp * mlxsw_sp)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 
mlxsw_sp_devlink_traps_fini(struct mlxsw_sp * mlxsw_sp)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 
mlxsw_sp_trap_init(struct mlxsw_core * mlxsw_core,const struct devlink_trap * trap,void * trap_ctx)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 
mlxsw_sp_trap_fini(struct mlxsw_core * mlxsw_core,const struct devlink_trap * trap,void * trap_ctx)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 
mlxsw_sp_trap_action_set(struct mlxsw_core * mlxsw_core,const struct devlink_trap * trap,enum devlink_trap_action action)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
mlxsw_sp_trap_group_policer_init(struct mlxsw_sp * mlxsw_sp,const struct devlink_trap_group * group)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
__mlxsw_sp_trap_group_init(struct mlxsw_sp * mlxsw_sp,const struct devlink_trap_group * group)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 
mlxsw_sp_trap_group_init(struct mlxsw_core * mlxsw_core,const struct devlink_trap_group * group)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 }
269