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 
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 	devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port);
103 	consume_skb(skb);
104 }
105 
106 int mlxsw_sp_devlink_traps_init(struct mlxsw_sp *mlxsw_sp)
107 {
108 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
109 
110 	if (WARN_ON(ARRAY_SIZE(mlxsw_sp_listener_devlink_map) !=
111 		    ARRAY_SIZE(mlxsw_sp_listeners_arr)))
112 		return -EINVAL;
113 
114 	return devlink_traps_register(devlink, mlxsw_sp_traps_arr,
115 				      ARRAY_SIZE(mlxsw_sp_traps_arr),
116 				      mlxsw_sp);
117 }
118 
119 void mlxsw_sp_devlink_traps_fini(struct mlxsw_sp *mlxsw_sp)
120 {
121 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
122 
123 	devlink_traps_unregister(devlink, mlxsw_sp_traps_arr,
124 				 ARRAY_SIZE(mlxsw_sp_traps_arr));
125 }
126 
127 int mlxsw_sp_trap_init(struct mlxsw_core *mlxsw_core,
128 		       const struct devlink_trap *trap, void *trap_ctx)
129 {
130 	int i;
131 
132 	for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) {
133 		struct mlxsw_listener *listener;
134 		int err;
135 
136 		if (mlxsw_sp_listener_devlink_map[i] != trap->id)
137 			continue;
138 		listener = &mlxsw_sp_listeners_arr[i];
139 
140 		err = mlxsw_core_trap_register(mlxsw_core, listener, trap_ctx);
141 		if (err)
142 			return err;
143 	}
144 
145 	return 0;
146 }
147 
148 void mlxsw_sp_trap_fini(struct mlxsw_core *mlxsw_core,
149 			const struct devlink_trap *trap, void *trap_ctx)
150 {
151 	int i;
152 
153 	for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) {
154 		struct mlxsw_listener *listener;
155 
156 		if (mlxsw_sp_listener_devlink_map[i] != trap->id)
157 			continue;
158 		listener = &mlxsw_sp_listeners_arr[i];
159 
160 		mlxsw_core_trap_unregister(mlxsw_core, listener, trap_ctx);
161 	}
162 }
163 
164 int mlxsw_sp_trap_action_set(struct mlxsw_core *mlxsw_core,
165 			     const struct devlink_trap *trap,
166 			     enum devlink_trap_action action)
167 {
168 	int i;
169 
170 	for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener_devlink_map); i++) {
171 		enum mlxsw_reg_hpkt_action hw_action;
172 		struct mlxsw_listener *listener;
173 		int err;
174 
175 		if (mlxsw_sp_listener_devlink_map[i] != trap->id)
176 			continue;
177 		listener = &mlxsw_sp_listeners_arr[i];
178 
179 		switch (action) {
180 		case DEVLINK_TRAP_ACTION_DROP:
181 			hw_action = MLXSW_REG_HPKT_ACTION_SET_FW_DEFAULT;
182 			break;
183 		case DEVLINK_TRAP_ACTION_TRAP:
184 			hw_action = MLXSW_REG_HPKT_ACTION_TRAP_EXCEPTION_TO_CPU;
185 			break;
186 		default:
187 			return -EINVAL;
188 		}
189 
190 		err = mlxsw_core_trap_action_set(mlxsw_core, listener,
191 						 hw_action);
192 		if (err)
193 			return err;
194 	}
195 
196 	return 0;
197 }
198 
199 #define MLXSW_SP_DISCARD_POLICER_ID	(MLXSW_REG_HTGT_TRAP_GROUP_MAX + 1)
200 
201 static int
202 mlxsw_sp_trap_group_policer_init(struct mlxsw_sp *mlxsw_sp,
203 				 const struct devlink_trap_group *group)
204 {
205 	enum mlxsw_reg_qpcr_ir_units ir_units;
206 	char qpcr_pl[MLXSW_REG_QPCR_LEN];
207 	u16 policer_id;
208 	u8 burst_size;
209 	bool is_bytes;
210 	u32 rate;
211 
212 	switch (group->id) {
213 	case DEVLINK_TRAP_GROUP_GENERIC_ID_L2_DROPS:
214 		policer_id = MLXSW_SP_DISCARD_POLICER_ID;
215 		ir_units = MLXSW_REG_QPCR_IR_UNITS_M;
216 		is_bytes = false;
217 		rate = 10 * 1024; /* 10Kpps */
218 		burst_size = 7;
219 		break;
220 	default:
221 		return -EINVAL;
222 	}
223 
224 	mlxsw_reg_qpcr_pack(qpcr_pl, policer_id, ir_units, is_bytes, rate,
225 			    burst_size);
226 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
227 }
228 
229 static int
230 __mlxsw_sp_trap_group_init(struct mlxsw_sp *mlxsw_sp,
231 			   const struct devlink_trap_group *group)
232 {
233 	char htgt_pl[MLXSW_REG_HTGT_LEN];
234 	u8 priority, tc, group_id;
235 	u16 policer_id;
236 
237 	switch (group->id) {
238 	case DEVLINK_TRAP_GROUP_GENERIC_ID_L2_DROPS:
239 		group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L2_DISCARDS;
240 		policer_id = MLXSW_SP_DISCARD_POLICER_ID;
241 		priority = 0;
242 		tc = 1;
243 		break;
244 	default:
245 		return -EINVAL;
246 	}
247 
248 	mlxsw_reg_htgt_pack(htgt_pl, group_id, policer_id, priority, tc);
249 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl);
250 }
251 
252 int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
253 			     const struct devlink_trap_group *group)
254 {
255 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
256 	int err;
257 
258 	err = mlxsw_sp_trap_group_policer_init(mlxsw_sp, group);
259 	if (err)
260 		return err;
261 
262 	err = __mlxsw_sp_trap_group_init(mlxsw_sp, group);
263 	if (err)
264 		return err;
265 
266 	return 0;
267 }
268