1 | // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) |
2 | #include "dpaa2-eth.h" |
3 | /* Copyright 2020 NXP |
4 | */ |
5 | |
6 | #define DPAA2_ETH_TRAP_DROP(_id, _group_id) \ |
7 | DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ |
8 | DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, 0) |
9 | |
10 | static const struct devlink_trap_group dpaa2_eth_trap_groups_arr[] = { |
11 | DEVLINK_TRAP_GROUP_GENERIC(PARSER_ERROR_DROPS, 0), |
12 | }; |
13 | |
14 | static const struct devlink_trap dpaa2_eth_traps_arr[] = { |
15 | DPAA2_ETH_TRAP_DROP(VXLAN_PARSING, PARSER_ERROR_DROPS), |
16 | DPAA2_ETH_TRAP_DROP(LLC_SNAP_PARSING, PARSER_ERROR_DROPS), |
17 | DPAA2_ETH_TRAP_DROP(VLAN_PARSING, PARSER_ERROR_DROPS), |
18 | DPAA2_ETH_TRAP_DROP(PPPOE_PPP_PARSING, PARSER_ERROR_DROPS), |
19 | DPAA2_ETH_TRAP_DROP(MPLS_PARSING, PARSER_ERROR_DROPS), |
20 | DPAA2_ETH_TRAP_DROP(ARP_PARSING, PARSER_ERROR_DROPS), |
21 | DPAA2_ETH_TRAP_DROP(IP_1_PARSING, PARSER_ERROR_DROPS), |
22 | DPAA2_ETH_TRAP_DROP(IP_N_PARSING, PARSER_ERROR_DROPS), |
23 | DPAA2_ETH_TRAP_DROP(GRE_PARSING, PARSER_ERROR_DROPS), |
24 | DPAA2_ETH_TRAP_DROP(UDP_PARSING, PARSER_ERROR_DROPS), |
25 | DPAA2_ETH_TRAP_DROP(TCP_PARSING, PARSER_ERROR_DROPS), |
26 | DPAA2_ETH_TRAP_DROP(IPSEC_PARSING, PARSER_ERROR_DROPS), |
27 | DPAA2_ETH_TRAP_DROP(SCTP_PARSING, PARSER_ERROR_DROPS), |
28 | DPAA2_ETH_TRAP_DROP(DCCP_PARSING, PARSER_ERROR_DROPS), |
29 | DPAA2_ETH_TRAP_DROP(GTP_PARSING, PARSER_ERROR_DROPS), |
30 | DPAA2_ETH_TRAP_DROP(ESP_PARSING, PARSER_ERROR_DROPS), |
31 | }; |
32 | |
33 | static int dpaa2_eth_dl_info_get(struct devlink *devlink, |
34 | struct devlink_info_req *req, |
35 | struct netlink_ext_ack *extack) |
36 | { |
37 | struct dpaa2_eth_devlink_priv *dl_priv = devlink_priv(devlink); |
38 | struct dpaa2_eth_priv *priv = dl_priv->dpaa2_priv; |
39 | char buf[10]; |
40 | |
41 | scnprintf(buf, size: 10, fmt: "%d.%d" , priv->dpni_ver_major, priv->dpni_ver_minor); |
42 | return devlink_info_version_running_put(req, version_name: "dpni" , version_value: buf); |
43 | } |
44 | |
45 | static struct dpaa2_eth_trap_item * |
46 | dpaa2_eth_dl_trap_item_lookup(struct dpaa2_eth_priv *priv, u16 trap_id) |
47 | { |
48 | struct dpaa2_eth_trap_data *dpaa2_eth_trap_data = priv->trap_data; |
49 | int i; |
50 | |
51 | for (i = 0; i < ARRAY_SIZE(dpaa2_eth_traps_arr); i++) { |
52 | if (dpaa2_eth_traps_arr[i].id == trap_id) |
53 | return &dpaa2_eth_trap_data->trap_items_arr[i]; |
54 | } |
55 | |
56 | return NULL; |
57 | } |
58 | |
59 | struct dpaa2_eth_trap_item *dpaa2_eth_dl_get_trap(struct dpaa2_eth_priv *priv, |
60 | struct dpaa2_fapr *fapr) |
61 | { |
62 | static const struct dpaa2_faf_error_bit { |
63 | int position; |
64 | enum devlink_trap_generic_id trap_id; |
65 | } faf_bits[] = { |
66 | { .position = 5, .trap_id = DEVLINK_TRAP_GENERIC_ID_VXLAN_PARSING }, |
67 | { .position = 20, .trap_id = DEVLINK_TRAP_GENERIC_ID_LLC_SNAP_PARSING }, |
68 | { .position = 24, .trap_id = DEVLINK_TRAP_GENERIC_ID_VLAN_PARSING }, |
69 | { .position = 26, .trap_id = DEVLINK_TRAP_GENERIC_ID_PPPOE_PPP_PARSING }, |
70 | { .position = 29, .trap_id = DEVLINK_TRAP_GENERIC_ID_MPLS_PARSING }, |
71 | { .position = 31, .trap_id = DEVLINK_TRAP_GENERIC_ID_ARP_PARSING }, |
72 | { .position = 52, .trap_id = DEVLINK_TRAP_GENERIC_ID_IP_1_PARSING }, |
73 | { .position = 61, .trap_id = DEVLINK_TRAP_GENERIC_ID_IP_N_PARSING }, |
74 | { .position = 67, .trap_id = DEVLINK_TRAP_GENERIC_ID_GRE_PARSING }, |
75 | { .position = 71, .trap_id = DEVLINK_TRAP_GENERIC_ID_UDP_PARSING }, |
76 | { .position = 76, .trap_id = DEVLINK_TRAP_GENERIC_ID_TCP_PARSING }, |
77 | { .position = 80, .trap_id = DEVLINK_TRAP_GENERIC_ID_IPSEC_PARSING }, |
78 | { .position = 82, .trap_id = DEVLINK_TRAP_GENERIC_ID_SCTP_PARSING }, |
79 | { .position = 84, .trap_id = DEVLINK_TRAP_GENERIC_ID_DCCP_PARSING }, |
80 | { .position = 88, .trap_id = DEVLINK_TRAP_GENERIC_ID_GTP_PARSING }, |
81 | { .position = 90, .trap_id = DEVLINK_TRAP_GENERIC_ID_ESP_PARSING }, |
82 | }; |
83 | u64 faf_word; |
84 | u64 mask; |
85 | int i; |
86 | |
87 | for (i = 0; i < ARRAY_SIZE(faf_bits); i++) { |
88 | if (faf_bits[i].position < 32) { |
89 | /* Low part of FAF. |
90 | * position ranges from 31 to 0, mask from 0 to 31. |
91 | */ |
92 | mask = 1ull << (31 - faf_bits[i].position); |
93 | faf_word = __le32_to_cpu(fapr->faf_lo); |
94 | } else { |
95 | /* High part of FAF. |
96 | * position ranges from 95 to 32, mask from 0 to 63. |
97 | */ |
98 | mask = 1ull << (63 - (faf_bits[i].position - 32)); |
99 | faf_word = __le64_to_cpu(fapr->faf_hi); |
100 | } |
101 | if (faf_word & mask) |
102 | return dpaa2_eth_dl_trap_item_lookup(priv, trap_id: faf_bits[i].trap_id); |
103 | } |
104 | return NULL; |
105 | } |
106 | |
107 | static int dpaa2_eth_dl_trap_init(struct devlink *devlink, |
108 | const struct devlink_trap *trap, |
109 | void *trap_ctx) |
110 | { |
111 | struct dpaa2_eth_devlink_priv *dl_priv = devlink_priv(devlink); |
112 | struct dpaa2_eth_priv *priv = dl_priv->dpaa2_priv; |
113 | struct dpaa2_eth_trap_item *dpaa2_eth_trap_item; |
114 | |
115 | dpaa2_eth_trap_item = dpaa2_eth_dl_trap_item_lookup(priv, trap_id: trap->id); |
116 | if (WARN_ON(!dpaa2_eth_trap_item)) |
117 | return -ENOENT; |
118 | |
119 | dpaa2_eth_trap_item->trap_ctx = trap_ctx; |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | static int dpaa2_eth_dl_trap_action_set(struct devlink *devlink, |
125 | const struct devlink_trap *trap, |
126 | enum devlink_trap_action action, |
127 | struct netlink_ext_ack *extack) |
128 | { |
129 | /* No support for changing the action of an independent packet trap, |
130 | * only per trap group - parser error drops |
131 | */ |
132 | NL_SET_ERR_MSG_MOD(extack, |
133 | "Cannot change trap action independently of group" ); |
134 | return -EOPNOTSUPP; |
135 | } |
136 | |
137 | static int dpaa2_eth_dl_trap_group_action_set(struct devlink *devlink, |
138 | const struct devlink_trap_group *group, |
139 | enum devlink_trap_action action, |
140 | struct netlink_ext_ack *extack) |
141 | { |
142 | struct dpaa2_eth_devlink_priv *dl_priv = devlink_priv(devlink); |
143 | struct dpaa2_eth_priv *priv = dl_priv->dpaa2_priv; |
144 | struct net_device *net_dev = priv->net_dev; |
145 | struct device *dev = net_dev->dev.parent; |
146 | struct dpni_error_cfg err_cfg = {0}; |
147 | int err; |
148 | |
149 | if (group->id != DEVLINK_TRAP_GROUP_GENERIC_ID_PARSER_ERROR_DROPS) |
150 | return -EOPNOTSUPP; |
151 | |
152 | /* Configure handling of frames marked as errors from the parser */ |
153 | err_cfg.errors = DPAA2_FAS_RX_ERR_MASK; |
154 | err_cfg.set_frame_annotation = 1; |
155 | |
156 | switch (action) { |
157 | case DEVLINK_TRAP_ACTION_DROP: |
158 | err_cfg.error_action = DPNI_ERROR_ACTION_DISCARD; |
159 | break; |
160 | case DEVLINK_TRAP_ACTION_TRAP: |
161 | err_cfg.error_action = DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE; |
162 | break; |
163 | default: |
164 | return -EOPNOTSUPP; |
165 | } |
166 | |
167 | err = dpni_set_errors_behavior(mc_io: priv->mc_io, cmd_flags: 0, token: priv->mc_token, cfg: &err_cfg); |
168 | if (err) { |
169 | dev_err(dev, "dpni_set_errors_behavior failed\n" ); |
170 | return err; |
171 | } |
172 | |
173 | return 0; |
174 | } |
175 | |
176 | static const struct devlink_ops dpaa2_eth_devlink_ops = { |
177 | .info_get = dpaa2_eth_dl_info_get, |
178 | .trap_init = dpaa2_eth_dl_trap_init, |
179 | .trap_action_set = dpaa2_eth_dl_trap_action_set, |
180 | .trap_group_action_set = dpaa2_eth_dl_trap_group_action_set, |
181 | }; |
182 | |
183 | int dpaa2_eth_dl_alloc(struct dpaa2_eth_priv *priv) |
184 | { |
185 | struct net_device *net_dev = priv->net_dev; |
186 | struct device *dev = net_dev->dev.parent; |
187 | struct dpaa2_eth_devlink_priv *dl_priv; |
188 | |
189 | priv->devlink = |
190 | devlink_alloc(ops: &dpaa2_eth_devlink_ops, priv_size: sizeof(*dl_priv), dev); |
191 | if (!priv->devlink) { |
192 | dev_err(dev, "devlink_alloc failed\n" ); |
193 | return -ENOMEM; |
194 | } |
195 | dl_priv = devlink_priv(devlink: priv->devlink); |
196 | dl_priv->dpaa2_priv = priv; |
197 | return 0; |
198 | } |
199 | |
200 | void dpaa2_eth_dl_free(struct dpaa2_eth_priv *priv) |
201 | { |
202 | devlink_free(devlink: priv->devlink); |
203 | } |
204 | |
205 | |
206 | void dpaa2_eth_dl_register(struct dpaa2_eth_priv *priv) |
207 | { |
208 | devlink_register(devlink: priv->devlink); |
209 | } |
210 | |
211 | void dpaa2_eth_dl_unregister(struct dpaa2_eth_priv *priv) |
212 | { |
213 | devlink_unregister(devlink: priv->devlink); |
214 | } |
215 | |
216 | int dpaa2_eth_dl_port_add(struct dpaa2_eth_priv *priv) |
217 | { |
218 | struct devlink_port *devlink_port = &priv->devlink_port; |
219 | struct devlink_port_attrs attrs = {}; |
220 | |
221 | attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; |
222 | devlink_port_attrs_set(devlink_port, devlink_port_attrs: &attrs); |
223 | return devlink_port_register(devlink: priv->devlink, devlink_port, port_index: 0); |
224 | } |
225 | |
226 | void dpaa2_eth_dl_port_del(struct dpaa2_eth_priv *priv) |
227 | { |
228 | struct devlink_port *devlink_port = &priv->devlink_port; |
229 | |
230 | devlink_port_unregister(devlink_port); |
231 | } |
232 | |
233 | int dpaa2_eth_dl_traps_register(struct dpaa2_eth_priv *priv) |
234 | { |
235 | struct dpaa2_eth_trap_data *dpaa2_eth_trap_data; |
236 | struct net_device *net_dev = priv->net_dev; |
237 | struct device *dev = net_dev->dev.parent; |
238 | int err; |
239 | |
240 | dpaa2_eth_trap_data = kzalloc(size: sizeof(*dpaa2_eth_trap_data), GFP_KERNEL); |
241 | if (!dpaa2_eth_trap_data) |
242 | return -ENOMEM; |
243 | priv->trap_data = dpaa2_eth_trap_data; |
244 | |
245 | dpaa2_eth_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(dpaa2_eth_traps_arr), |
246 | size: sizeof(struct dpaa2_eth_trap_item), |
247 | GFP_KERNEL); |
248 | if (!dpaa2_eth_trap_data->trap_items_arr) { |
249 | err = -ENOMEM; |
250 | goto trap_data_free; |
251 | } |
252 | |
253 | err = devlink_trap_groups_register(devlink: priv->devlink, groups: dpaa2_eth_trap_groups_arr, |
254 | ARRAY_SIZE(dpaa2_eth_trap_groups_arr)); |
255 | if (err) { |
256 | dev_err(dev, "devlink_trap_groups_register() = %d\n" , err); |
257 | goto trap_items_arr_free; |
258 | } |
259 | |
260 | err = devlink_traps_register(devlink: priv->devlink, traps: dpaa2_eth_traps_arr, |
261 | ARRAY_SIZE(dpaa2_eth_traps_arr), priv); |
262 | if (err) { |
263 | dev_err(dev, "devlink_traps_register() = %d\n" , err); |
264 | goto trap_groups_unregiser; |
265 | } |
266 | |
267 | return 0; |
268 | |
269 | trap_groups_unregiser: |
270 | devlink_trap_groups_unregister(devlink: priv->devlink, groups: dpaa2_eth_trap_groups_arr, |
271 | ARRAY_SIZE(dpaa2_eth_trap_groups_arr)); |
272 | trap_items_arr_free: |
273 | kfree(objp: dpaa2_eth_trap_data->trap_items_arr); |
274 | trap_data_free: |
275 | kfree(objp: dpaa2_eth_trap_data); |
276 | priv->trap_data = NULL; |
277 | |
278 | return err; |
279 | } |
280 | |
281 | void dpaa2_eth_dl_traps_unregister(struct dpaa2_eth_priv *priv) |
282 | { |
283 | devlink_traps_unregister(devlink: priv->devlink, traps: dpaa2_eth_traps_arr, |
284 | ARRAY_SIZE(dpaa2_eth_traps_arr)); |
285 | devlink_trap_groups_unregister(devlink: priv->devlink, groups: dpaa2_eth_trap_groups_arr, |
286 | ARRAY_SIZE(dpaa2_eth_trap_groups_arr)); |
287 | kfree(objp: priv->trap_data->trap_items_arr); |
288 | kfree(objp: priv->trap_data); |
289 | } |
290 | |