1 | // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
2 | /* Copyright (c) 2019 Mellanox Technologies */ |
3 | |
4 | #include <devlink.h> |
5 | |
6 | #include "mlx5_core.h" |
7 | #include "fw_reset.h" |
8 | #include "fs_core.h" |
9 | #include "eswitch.h" |
10 | #include "esw/qos.h" |
11 | #include "sf/dev/dev.h" |
12 | #include "sf/sf.h" |
13 | |
14 | static int mlx5_devlink_flash_update(struct devlink *devlink, |
15 | struct devlink_flash_update_params *params, |
16 | struct netlink_ext_ack *extack) |
17 | { |
18 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
19 | |
20 | return mlx5_firmware_flash(dev, fw: params->fw, extack); |
21 | } |
22 | |
23 | static u8 mlx5_fw_ver_major(u32 version) |
24 | { |
25 | return (version >> 24) & 0xff; |
26 | } |
27 | |
28 | static u8 mlx5_fw_ver_minor(u32 version) |
29 | { |
30 | return (version >> 16) & 0xff; |
31 | } |
32 | |
33 | static u16 mlx5_fw_ver_subminor(u32 version) |
34 | { |
35 | return version & 0xffff; |
36 | } |
37 | |
38 | #define DEVLINK_FW_STRING_LEN 32 |
39 | |
40 | static int |
41 | mlx5_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, |
42 | struct netlink_ext_ack *extack) |
43 | { |
44 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
45 | char version_str[DEVLINK_FW_STRING_LEN]; |
46 | u32 running_fw, stored_fw; |
47 | int err; |
48 | |
49 | err = devlink_info_version_fixed_put(req, version_name: "fw.psid" , version_value: dev->board_id); |
50 | if (err) |
51 | return err; |
52 | |
53 | err = mlx5_fw_version_query(dev, running_ver: &running_fw, stored_ver: &stored_fw); |
54 | if (err) |
55 | return err; |
56 | |
57 | snprintf(buf: version_str, size: sizeof(version_str), fmt: "%d.%d.%04d" , |
58 | mlx5_fw_ver_major(version: running_fw), mlx5_fw_ver_minor(version: running_fw), |
59 | mlx5_fw_ver_subminor(version: running_fw)); |
60 | err = devlink_info_version_running_put(req, version_name: "fw.version" , version_value: version_str); |
61 | if (err) |
62 | return err; |
63 | err = devlink_info_version_running_put(req, |
64 | DEVLINK_INFO_VERSION_GENERIC_FW, |
65 | version_value: version_str); |
66 | if (err) |
67 | return err; |
68 | |
69 | /* no pending version, return running (stored) version */ |
70 | if (stored_fw == 0) |
71 | stored_fw = running_fw; |
72 | |
73 | snprintf(buf: version_str, size: sizeof(version_str), fmt: "%d.%d.%04d" , |
74 | mlx5_fw_ver_major(version: stored_fw), mlx5_fw_ver_minor(version: stored_fw), |
75 | mlx5_fw_ver_subminor(version: stored_fw)); |
76 | err = devlink_info_version_stored_put(req, version_name: "fw.version" , version_value: version_str); |
77 | if (err) |
78 | return err; |
79 | return devlink_info_version_stored_put(req, |
80 | DEVLINK_INFO_VERSION_GENERIC_FW, |
81 | version_value: version_str); |
82 | } |
83 | |
84 | static int mlx5_devlink_reload_fw_activate(struct devlink *devlink, struct netlink_ext_ack *extack) |
85 | { |
86 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
87 | u8 reset_level, reset_type, net_port_alive; |
88 | int err; |
89 | |
90 | err = mlx5_fw_reset_query(dev, reset_level: &reset_level, reset_type: &reset_type); |
91 | if (err) |
92 | return err; |
93 | if (!(reset_level & MLX5_MFRL_REG_RESET_LEVEL3)) { |
94 | NL_SET_ERR_MSG_MOD(extack, "FW activate requires reboot" ); |
95 | return -EINVAL; |
96 | } |
97 | |
98 | net_port_alive = !!(reset_type & MLX5_MFRL_REG_RESET_TYPE_NET_PORT_ALIVE); |
99 | err = mlx5_fw_reset_set_reset_sync(dev, reset_type_sel: net_port_alive, extack); |
100 | if (err) |
101 | return err; |
102 | |
103 | err = mlx5_fw_reset_wait_reset_done(dev); |
104 | if (err) |
105 | return err; |
106 | |
107 | mlx5_unload_one_devl_locked(dev, suspend: true); |
108 | err = mlx5_health_wait_pci_up(dev); |
109 | if (err) |
110 | NL_SET_ERR_MSG_MOD(extack, "FW activate aborted, PCI reads fail after reset" ); |
111 | |
112 | return err; |
113 | } |
114 | |
115 | static int mlx5_devlink_trigger_fw_live_patch(struct devlink *devlink, |
116 | struct netlink_ext_ack *extack) |
117 | { |
118 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
119 | u8 reset_level; |
120 | int err; |
121 | |
122 | err = mlx5_fw_reset_query(dev, reset_level: &reset_level, NULL); |
123 | if (err) |
124 | return err; |
125 | if (!(reset_level & MLX5_MFRL_REG_RESET_LEVEL0)) { |
126 | NL_SET_ERR_MSG_MOD(extack, |
127 | "FW upgrade to the stored FW can't be done by FW live patching" ); |
128 | return -EINVAL; |
129 | } |
130 | |
131 | return mlx5_fw_reset_set_live_patch(dev); |
132 | } |
133 | |
134 | static int mlx5_devlink_reload_down(struct devlink *devlink, bool netns_change, |
135 | enum devlink_reload_action action, |
136 | enum devlink_reload_limit limit, |
137 | struct netlink_ext_ack *extack) |
138 | { |
139 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
140 | struct pci_dev *pdev = dev->pdev; |
141 | int ret = 0; |
142 | |
143 | if (mlx5_dev_is_lightweight(dev)) { |
144 | if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
145 | return -EOPNOTSUPP; |
146 | mlx5_unload_one_light(dev); |
147 | return 0; |
148 | } |
149 | |
150 | if (mlx5_lag_is_active(dev)) { |
151 | NL_SET_ERR_MSG_MOD(extack, "reload is unsupported in Lag mode" ); |
152 | return -EOPNOTSUPP; |
153 | } |
154 | |
155 | if (mlx5_core_is_mp_slave(dev)) { |
156 | NL_SET_ERR_MSG_MOD(extack, "reload is unsupported for multi port slave" ); |
157 | return -EOPNOTSUPP; |
158 | } |
159 | |
160 | if (action == DEVLINK_RELOAD_ACTION_FW_ACTIVATE && |
161 | !dev->priv.fw_reset) { |
162 | NL_SET_ERR_MSG_MOD(extack, "FW activate is unsupported for this function" ); |
163 | return -EOPNOTSUPP; |
164 | } |
165 | |
166 | if (mlx5_core_is_pf(dev) && pci_num_vf(dev: pdev)) |
167 | NL_SET_ERR_MSG_MOD(extack, "reload while VFs are present is unfavorable" ); |
168 | |
169 | switch (action) { |
170 | case DEVLINK_RELOAD_ACTION_DRIVER_REINIT: |
171 | mlx5_unload_one_devl_locked(dev, suspend: false); |
172 | break; |
173 | case DEVLINK_RELOAD_ACTION_FW_ACTIVATE: |
174 | if (limit == DEVLINK_RELOAD_LIMIT_NO_RESET) |
175 | ret = mlx5_devlink_trigger_fw_live_patch(devlink, extack); |
176 | else |
177 | ret = mlx5_devlink_reload_fw_activate(devlink, extack); |
178 | break; |
179 | default: |
180 | /* Unsupported action should not get to this function */ |
181 | WARN_ON(1); |
182 | ret = -EOPNOTSUPP; |
183 | } |
184 | |
185 | return ret; |
186 | } |
187 | |
188 | static int mlx5_devlink_reload_up(struct devlink *devlink, enum devlink_reload_action action, |
189 | enum devlink_reload_limit limit, u32 *actions_performed, |
190 | struct netlink_ext_ack *extack) |
191 | { |
192 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
193 | int ret = 0; |
194 | |
195 | *actions_performed = BIT(action); |
196 | switch (action) { |
197 | case DEVLINK_RELOAD_ACTION_DRIVER_REINIT: |
198 | if (mlx5_dev_is_lightweight(dev)) { |
199 | mlx5_fw_reporters_create(dev); |
200 | return mlx5_init_one_devl_locked(dev); |
201 | } |
202 | ret = mlx5_load_one_devl_locked(dev, recovery: false); |
203 | break; |
204 | case DEVLINK_RELOAD_ACTION_FW_ACTIVATE: |
205 | if (limit == DEVLINK_RELOAD_LIMIT_NO_RESET) |
206 | break; |
207 | /* On fw_activate action, also driver is reloaded and reinit performed */ |
208 | *actions_performed |= BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT); |
209 | ret = mlx5_load_one_devl_locked(dev, recovery: true); |
210 | if (ret) |
211 | return ret; |
212 | ret = mlx5_fw_reset_verify_fw_complete(dev, extack); |
213 | break; |
214 | default: |
215 | /* Unsupported action should not get to this function */ |
216 | WARN_ON(1); |
217 | ret = -EOPNOTSUPP; |
218 | } |
219 | |
220 | return ret; |
221 | } |
222 | |
223 | static struct mlx5_devlink_trap *mlx5_find_trap_by_id(struct mlx5_core_dev *dev, int trap_id) |
224 | { |
225 | struct mlx5_devlink_trap *dl_trap; |
226 | |
227 | list_for_each_entry(dl_trap, &dev->priv.traps, list) |
228 | if (dl_trap->trap.id == trap_id) |
229 | return dl_trap; |
230 | |
231 | return NULL; |
232 | } |
233 | |
234 | static int mlx5_devlink_trap_init(struct devlink *devlink, const struct devlink_trap *trap, |
235 | void *trap_ctx) |
236 | { |
237 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
238 | struct mlx5_devlink_trap *dl_trap; |
239 | |
240 | dl_trap = kzalloc(size: sizeof(*dl_trap), GFP_KERNEL); |
241 | if (!dl_trap) |
242 | return -ENOMEM; |
243 | |
244 | dl_trap->trap.id = trap->id; |
245 | dl_trap->trap.action = DEVLINK_TRAP_ACTION_DROP; |
246 | dl_trap->item = trap_ctx; |
247 | |
248 | if (mlx5_find_trap_by_id(dev, trap_id: trap->id)) { |
249 | kfree(objp: dl_trap); |
250 | mlx5_core_err(dev, "Devlink trap: Trap 0x%x already found" , trap->id); |
251 | return -EEXIST; |
252 | } |
253 | |
254 | list_add_tail(new: &dl_trap->list, head: &dev->priv.traps); |
255 | return 0; |
256 | } |
257 | |
258 | static void mlx5_devlink_trap_fini(struct devlink *devlink, const struct devlink_trap *trap, |
259 | void *trap_ctx) |
260 | { |
261 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
262 | struct mlx5_devlink_trap *dl_trap; |
263 | |
264 | dl_trap = mlx5_find_trap_by_id(dev, trap_id: trap->id); |
265 | if (!dl_trap) { |
266 | mlx5_core_err(dev, "Devlink trap: Missing trap id 0x%x" , trap->id); |
267 | return; |
268 | } |
269 | list_del(entry: &dl_trap->list); |
270 | kfree(objp: dl_trap); |
271 | } |
272 | |
273 | static int mlx5_devlink_trap_action_set(struct devlink *devlink, |
274 | const struct devlink_trap *trap, |
275 | enum devlink_trap_action action, |
276 | struct netlink_ext_ack *extack) |
277 | { |
278 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
279 | struct mlx5_devlink_trap_event_ctx trap_event_ctx; |
280 | enum devlink_trap_action action_orig; |
281 | struct mlx5_devlink_trap *dl_trap; |
282 | int err; |
283 | |
284 | if (is_mdev_switchdev_mode(dev)) { |
285 | NL_SET_ERR_MSG_MOD(extack, "Devlink traps can't be set in switchdev mode" ); |
286 | return -EOPNOTSUPP; |
287 | } |
288 | |
289 | dl_trap = mlx5_find_trap_by_id(dev, trap_id: trap->id); |
290 | if (!dl_trap) { |
291 | mlx5_core_err(dev, "Devlink trap: Set action on invalid trap id 0x%x" , trap->id); |
292 | return -EINVAL; |
293 | } |
294 | |
295 | if (action != DEVLINK_TRAP_ACTION_DROP && action != DEVLINK_TRAP_ACTION_TRAP) |
296 | return -EOPNOTSUPP; |
297 | |
298 | if (action == dl_trap->trap.action) |
299 | return 0; |
300 | |
301 | action_orig = dl_trap->trap.action; |
302 | dl_trap->trap.action = action; |
303 | trap_event_ctx.trap = &dl_trap->trap; |
304 | trap_event_ctx.err = 0; |
305 | err = mlx5_blocking_notifier_call_chain(dev, event: MLX5_DRIVER_EVENT_TYPE_TRAP, |
306 | data: &trap_event_ctx); |
307 | if (err == NOTIFY_BAD) |
308 | dl_trap->trap.action = action_orig; |
309 | |
310 | return trap_event_ctx.err; |
311 | } |
312 | |
313 | static const struct devlink_ops mlx5_devlink_ops = { |
314 | #ifdef CONFIG_MLX5_ESWITCH |
315 | .eswitch_mode_set = mlx5_devlink_eswitch_mode_set, |
316 | .eswitch_mode_get = mlx5_devlink_eswitch_mode_get, |
317 | .eswitch_inline_mode_set = mlx5_devlink_eswitch_inline_mode_set, |
318 | .eswitch_inline_mode_get = mlx5_devlink_eswitch_inline_mode_get, |
319 | .eswitch_encap_mode_set = mlx5_devlink_eswitch_encap_mode_set, |
320 | .eswitch_encap_mode_get = mlx5_devlink_eswitch_encap_mode_get, |
321 | .rate_leaf_tx_share_set = mlx5_esw_devlink_rate_leaf_tx_share_set, |
322 | .rate_leaf_tx_max_set = mlx5_esw_devlink_rate_leaf_tx_max_set, |
323 | .rate_node_tx_share_set = mlx5_esw_devlink_rate_node_tx_share_set, |
324 | .rate_node_tx_max_set = mlx5_esw_devlink_rate_node_tx_max_set, |
325 | .rate_node_new = mlx5_esw_devlink_rate_node_new, |
326 | .rate_node_del = mlx5_esw_devlink_rate_node_del, |
327 | .rate_leaf_parent_set = mlx5_esw_devlink_rate_parent_set, |
328 | #endif |
329 | #ifdef CONFIG_MLX5_SF_MANAGER |
330 | .port_new = mlx5_devlink_sf_port_new, |
331 | #endif |
332 | .flash_update = mlx5_devlink_flash_update, |
333 | .info_get = mlx5_devlink_info_get, |
334 | .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) | |
335 | BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE), |
336 | .reload_limits = BIT(DEVLINK_RELOAD_LIMIT_NO_RESET), |
337 | .reload_down = mlx5_devlink_reload_down, |
338 | .reload_up = mlx5_devlink_reload_up, |
339 | .trap_init = mlx5_devlink_trap_init, |
340 | .trap_fini = mlx5_devlink_trap_fini, |
341 | .trap_action_set = mlx5_devlink_trap_action_set, |
342 | }; |
343 | |
344 | void mlx5_devlink_trap_report(struct mlx5_core_dev *dev, int trap_id, struct sk_buff *skb, |
345 | struct devlink_port *dl_port) |
346 | { |
347 | struct devlink *devlink = priv_to_devlink(priv: dev); |
348 | struct mlx5_devlink_trap *dl_trap; |
349 | |
350 | dl_trap = mlx5_find_trap_by_id(dev, trap_id); |
351 | if (!dl_trap) { |
352 | mlx5_core_err(dev, "Devlink trap: Report on invalid trap id 0x%x" , trap_id); |
353 | return; |
354 | } |
355 | |
356 | if (dl_trap->trap.action != DEVLINK_TRAP_ACTION_TRAP) { |
357 | mlx5_core_dbg(dev, "Devlink trap: Trap id %d has action %d" , trap_id, |
358 | dl_trap->trap.action); |
359 | return; |
360 | } |
361 | devlink_trap_report(devlink, skb, trap_ctx: dl_trap->item, in_devlink_port: dl_port, NULL); |
362 | } |
363 | |
364 | int mlx5_devlink_trap_get_num_active(struct mlx5_core_dev *dev) |
365 | { |
366 | struct mlx5_devlink_trap *dl_trap; |
367 | int count = 0; |
368 | |
369 | list_for_each_entry(dl_trap, &dev->priv.traps, list) |
370 | if (dl_trap->trap.action == DEVLINK_TRAP_ACTION_TRAP) |
371 | count++; |
372 | |
373 | return count; |
374 | } |
375 | |
376 | int mlx5_devlink_traps_get_action(struct mlx5_core_dev *dev, int trap_id, |
377 | enum devlink_trap_action *action) |
378 | { |
379 | struct mlx5_devlink_trap *dl_trap; |
380 | |
381 | dl_trap = mlx5_find_trap_by_id(dev, trap_id); |
382 | if (!dl_trap) { |
383 | mlx5_core_err(dev, "Devlink trap: Get action on invalid trap id 0x%x" , |
384 | trap_id); |
385 | return -EINVAL; |
386 | } |
387 | |
388 | *action = dl_trap->trap.action; |
389 | return 0; |
390 | } |
391 | |
392 | struct devlink *mlx5_devlink_alloc(struct device *dev) |
393 | { |
394 | return devlink_alloc(ops: &mlx5_devlink_ops, priv_size: sizeof(struct mlx5_core_dev), |
395 | dev); |
396 | } |
397 | |
398 | void mlx5_devlink_free(struct devlink *devlink) |
399 | { |
400 | devlink_free(devlink); |
401 | } |
402 | |
403 | static int mlx5_devlink_enable_roce_validate(struct devlink *devlink, u32 id, |
404 | union devlink_param_value val, |
405 | struct netlink_ext_ack *extack) |
406 | { |
407 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
408 | bool new_state = val.vbool; |
409 | |
410 | if (new_state && !MLX5_CAP_GEN(dev, roce) && |
411 | !(MLX5_CAP_GEN(dev, roce_rw_supported) && MLX5_CAP_GEN_MAX(dev, roce))) { |
412 | NL_SET_ERR_MSG_MOD(extack, "Device doesn't support RoCE" ); |
413 | return -EOPNOTSUPP; |
414 | } |
415 | if (mlx5_core_is_mp_slave(dev) || mlx5_lag_is_active(dev)) { |
416 | NL_SET_ERR_MSG_MOD(extack, "Multi port slave/Lag device can't configure RoCE" ); |
417 | return -EOPNOTSUPP; |
418 | } |
419 | |
420 | return 0; |
421 | } |
422 | |
423 | #ifdef CONFIG_MLX5_ESWITCH |
424 | static int mlx5_devlink_large_group_num_validate(struct devlink *devlink, u32 id, |
425 | union devlink_param_value val, |
426 | struct netlink_ext_ack *extack) |
427 | { |
428 | int group_num = val.vu32; |
429 | |
430 | if (group_num < 1 || group_num > 1024) { |
431 | NL_SET_ERR_MSG_MOD(extack, |
432 | "Unsupported group number, supported range is 1-1024" ); |
433 | return -EOPNOTSUPP; |
434 | } |
435 | |
436 | return 0; |
437 | } |
438 | #endif |
439 | |
440 | static int mlx5_devlink_eq_depth_validate(struct devlink *devlink, u32 id, |
441 | union devlink_param_value val, |
442 | struct netlink_ext_ack *extack) |
443 | { |
444 | return (val.vu32 >= 64 && val.vu32 <= 4096) ? 0 : -EINVAL; |
445 | } |
446 | |
447 | static int |
448 | mlx5_devlink_hairpin_num_queues_validate(struct devlink *devlink, u32 id, |
449 | union devlink_param_value val, |
450 | struct netlink_ext_ack *extack) |
451 | { |
452 | return val.vu32 ? 0 : -EINVAL; |
453 | } |
454 | |
455 | static int |
456 | mlx5_devlink_hairpin_queue_size_validate(struct devlink *devlink, u32 id, |
457 | union devlink_param_value val, |
458 | struct netlink_ext_ack *extack) |
459 | { |
460 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
461 | u32 val32 = val.vu32; |
462 | |
463 | if (!is_power_of_2(n: val32)) { |
464 | NL_SET_ERR_MSG_MOD(extack, "Value is not power of two" ); |
465 | return -EINVAL; |
466 | } |
467 | |
468 | if (val32 > BIT(MLX5_CAP_GEN(dev, log_max_hairpin_num_packets))) { |
469 | NL_SET_ERR_MSG_FMT_MOD( |
470 | extack, "Maximum hairpin queue size is %lu" , |
471 | BIT(MLX5_CAP_GEN(dev, log_max_hairpin_num_packets))); |
472 | return -EINVAL; |
473 | } |
474 | |
475 | return 0; |
476 | } |
477 | |
478 | static void mlx5_devlink_hairpin_params_init_values(struct devlink *devlink) |
479 | { |
480 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
481 | union devlink_param_value value; |
482 | u32 link_speed = 0; |
483 | u64 link_speed64; |
484 | |
485 | /* set hairpin pair per each 50Gbs share of the link */ |
486 | mlx5_port_max_linkspeed(mdev: dev, speed: &link_speed); |
487 | link_speed = max_t(u32, link_speed, 50000); |
488 | link_speed64 = link_speed; |
489 | do_div(link_speed64, 50000); |
490 | |
491 | value.vu32 = link_speed64; |
492 | devl_param_driverinit_value_set( |
493 | devlink, param_id: MLX5_DEVLINK_PARAM_ID_HAIRPIN_NUM_QUEUES, init_val: value); |
494 | |
495 | value.vu32 = |
496 | BIT(min_t(u32, 16 - MLX5_MPWRQ_MIN_LOG_STRIDE_SZ(dev), |
497 | MLX5_CAP_GEN(dev, log_max_hairpin_num_packets))); |
498 | devl_param_driverinit_value_set( |
499 | devlink, param_id: MLX5_DEVLINK_PARAM_ID_HAIRPIN_QUEUE_SIZE, init_val: value); |
500 | } |
501 | |
502 | static const struct devlink_param mlx5_devlink_params[] = { |
503 | DEVLINK_PARAM_GENERIC(ENABLE_ROCE, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), |
504 | NULL, NULL, mlx5_devlink_enable_roce_validate), |
505 | #ifdef CONFIG_MLX5_ESWITCH |
506 | DEVLINK_PARAM_DRIVER(MLX5_DEVLINK_PARAM_ID_ESW_LARGE_GROUP_NUM, |
507 | "fdb_large_groups" , DEVLINK_PARAM_TYPE_U32, |
508 | BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), |
509 | NULL, NULL, |
510 | mlx5_devlink_large_group_num_validate), |
511 | #endif |
512 | DEVLINK_PARAM_GENERIC(IO_EQ_SIZE, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), |
513 | NULL, NULL, mlx5_devlink_eq_depth_validate), |
514 | DEVLINK_PARAM_GENERIC(EVENT_EQ_SIZE, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), |
515 | NULL, NULL, mlx5_devlink_eq_depth_validate), |
516 | }; |
517 | |
518 | static void mlx5_devlink_set_params_init_values(struct devlink *devlink) |
519 | { |
520 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
521 | union devlink_param_value value; |
522 | |
523 | value.vbool = MLX5_CAP_GEN(dev, roce) && !mlx5_dev_is_lightweight(dev); |
524 | devl_param_driverinit_value_set(devlink, |
525 | param_id: DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE, |
526 | init_val: value); |
527 | |
528 | #ifdef CONFIG_MLX5_ESWITCH |
529 | value.vu32 = ESW_OFFLOADS_DEFAULT_NUM_GROUPS; |
530 | devl_param_driverinit_value_set(devlink, |
531 | param_id: MLX5_DEVLINK_PARAM_ID_ESW_LARGE_GROUP_NUM, |
532 | init_val: value); |
533 | #endif |
534 | |
535 | value.vu32 = MLX5_COMP_EQ_SIZE; |
536 | devl_param_driverinit_value_set(devlink, |
537 | param_id: DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE, |
538 | init_val: value); |
539 | |
540 | value.vu32 = MLX5_NUM_ASYNC_EQE; |
541 | devl_param_driverinit_value_set(devlink, |
542 | param_id: DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE, |
543 | init_val: value); |
544 | } |
545 | |
546 | static const struct devlink_param mlx5_devlink_eth_params[] = { |
547 | DEVLINK_PARAM_GENERIC(ENABLE_ETH, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), |
548 | NULL, NULL, NULL), |
549 | DEVLINK_PARAM_DRIVER(MLX5_DEVLINK_PARAM_ID_HAIRPIN_NUM_QUEUES, |
550 | "hairpin_num_queues" , DEVLINK_PARAM_TYPE_U32, |
551 | BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), NULL, NULL, |
552 | mlx5_devlink_hairpin_num_queues_validate), |
553 | DEVLINK_PARAM_DRIVER(MLX5_DEVLINK_PARAM_ID_HAIRPIN_QUEUE_SIZE, |
554 | "hairpin_queue_size" , DEVLINK_PARAM_TYPE_U32, |
555 | BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), NULL, NULL, |
556 | mlx5_devlink_hairpin_queue_size_validate), |
557 | }; |
558 | |
559 | static int mlx5_devlink_eth_params_register(struct devlink *devlink) |
560 | { |
561 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
562 | union devlink_param_value value; |
563 | int err; |
564 | |
565 | if (!mlx5_eth_supported(dev)) |
566 | return 0; |
567 | |
568 | err = devl_params_register(devlink, params: mlx5_devlink_eth_params, |
569 | ARRAY_SIZE(mlx5_devlink_eth_params)); |
570 | if (err) |
571 | return err; |
572 | |
573 | value.vbool = !mlx5_dev_is_lightweight(dev); |
574 | devl_param_driverinit_value_set(devlink, |
575 | param_id: DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH, |
576 | init_val: value); |
577 | |
578 | mlx5_devlink_hairpin_params_init_values(devlink); |
579 | |
580 | return 0; |
581 | } |
582 | |
583 | static void mlx5_devlink_eth_params_unregister(struct devlink *devlink) |
584 | { |
585 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
586 | |
587 | if (!mlx5_eth_supported(dev)) |
588 | return; |
589 | |
590 | devl_params_unregister(devlink, params: mlx5_devlink_eth_params, |
591 | ARRAY_SIZE(mlx5_devlink_eth_params)); |
592 | } |
593 | |
594 | static int mlx5_devlink_enable_rdma_validate(struct devlink *devlink, u32 id, |
595 | union devlink_param_value val, |
596 | struct netlink_ext_ack *extack) |
597 | { |
598 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
599 | bool new_state = val.vbool; |
600 | |
601 | if (new_state && !mlx5_rdma_supported(dev)) |
602 | return -EOPNOTSUPP; |
603 | return 0; |
604 | } |
605 | |
606 | static const struct devlink_param mlx5_devlink_rdma_params[] = { |
607 | DEVLINK_PARAM_GENERIC(ENABLE_RDMA, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), |
608 | NULL, NULL, mlx5_devlink_enable_rdma_validate), |
609 | }; |
610 | |
611 | static int mlx5_devlink_rdma_params_register(struct devlink *devlink) |
612 | { |
613 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
614 | union devlink_param_value value; |
615 | int err; |
616 | |
617 | if (!IS_ENABLED(CONFIG_MLX5_INFINIBAND)) |
618 | return 0; |
619 | |
620 | err = devl_params_register(devlink, params: mlx5_devlink_rdma_params, |
621 | ARRAY_SIZE(mlx5_devlink_rdma_params)); |
622 | if (err) |
623 | return err; |
624 | |
625 | value.vbool = !mlx5_dev_is_lightweight(dev); |
626 | devl_param_driverinit_value_set(devlink, |
627 | param_id: DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA, |
628 | init_val: value); |
629 | return 0; |
630 | } |
631 | |
632 | static void mlx5_devlink_rdma_params_unregister(struct devlink *devlink) |
633 | { |
634 | if (!IS_ENABLED(CONFIG_MLX5_INFINIBAND)) |
635 | return; |
636 | |
637 | devl_params_unregister(devlink, params: mlx5_devlink_rdma_params, |
638 | ARRAY_SIZE(mlx5_devlink_rdma_params)); |
639 | } |
640 | |
641 | static const struct devlink_param mlx5_devlink_vnet_params[] = { |
642 | DEVLINK_PARAM_GENERIC(ENABLE_VNET, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), |
643 | NULL, NULL, NULL), |
644 | }; |
645 | |
646 | static int mlx5_devlink_vnet_params_register(struct devlink *devlink) |
647 | { |
648 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
649 | union devlink_param_value value; |
650 | int err; |
651 | |
652 | if (!mlx5_vnet_supported(dev)) |
653 | return 0; |
654 | |
655 | err = devl_params_register(devlink, params: mlx5_devlink_vnet_params, |
656 | ARRAY_SIZE(mlx5_devlink_vnet_params)); |
657 | if (err) |
658 | return err; |
659 | |
660 | value.vbool = !mlx5_dev_is_lightweight(dev); |
661 | devl_param_driverinit_value_set(devlink, |
662 | param_id: DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET, |
663 | init_val: value); |
664 | return 0; |
665 | } |
666 | |
667 | static void mlx5_devlink_vnet_params_unregister(struct devlink *devlink) |
668 | { |
669 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
670 | |
671 | if (!mlx5_vnet_supported(dev)) |
672 | return; |
673 | |
674 | devl_params_unregister(devlink, params: mlx5_devlink_vnet_params, |
675 | ARRAY_SIZE(mlx5_devlink_vnet_params)); |
676 | } |
677 | |
678 | static int mlx5_devlink_auxdev_params_register(struct devlink *devlink) |
679 | { |
680 | int err; |
681 | |
682 | err = mlx5_devlink_eth_params_register(devlink); |
683 | if (err) |
684 | return err; |
685 | |
686 | err = mlx5_devlink_rdma_params_register(devlink); |
687 | if (err) |
688 | goto rdma_err; |
689 | |
690 | err = mlx5_devlink_vnet_params_register(devlink); |
691 | if (err) |
692 | goto vnet_err; |
693 | return 0; |
694 | |
695 | vnet_err: |
696 | mlx5_devlink_rdma_params_unregister(devlink); |
697 | rdma_err: |
698 | mlx5_devlink_eth_params_unregister(devlink); |
699 | return err; |
700 | } |
701 | |
702 | static void mlx5_devlink_auxdev_params_unregister(struct devlink *devlink) |
703 | { |
704 | mlx5_devlink_vnet_params_unregister(devlink); |
705 | mlx5_devlink_rdma_params_unregister(devlink); |
706 | mlx5_devlink_eth_params_unregister(devlink); |
707 | } |
708 | |
709 | static int mlx5_devlink_max_uc_list_validate(struct devlink *devlink, u32 id, |
710 | union devlink_param_value val, |
711 | struct netlink_ext_ack *extack) |
712 | { |
713 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
714 | |
715 | if (val.vu32 == 0) { |
716 | NL_SET_ERR_MSG_MOD(extack, "max_macs value must be greater than 0" ); |
717 | return -EINVAL; |
718 | } |
719 | |
720 | if (!is_power_of_2(n: val.vu32)) { |
721 | NL_SET_ERR_MSG_MOD(extack, "Only power of 2 values are supported for max_macs" ); |
722 | return -EINVAL; |
723 | } |
724 | |
725 | if (ilog2(val.vu32) > |
726 | MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list)) { |
727 | NL_SET_ERR_MSG_MOD(extack, "max_macs value is out of the supported range" ); |
728 | return -EINVAL; |
729 | } |
730 | |
731 | return 0; |
732 | } |
733 | |
734 | static const struct devlink_param mlx5_devlink_max_uc_list_params[] = { |
735 | DEVLINK_PARAM_GENERIC(MAX_MACS, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), |
736 | NULL, NULL, mlx5_devlink_max_uc_list_validate), |
737 | }; |
738 | |
739 | static int mlx5_devlink_max_uc_list_params_register(struct devlink *devlink) |
740 | { |
741 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
742 | union devlink_param_value value; |
743 | int err; |
744 | |
745 | if (!MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list_wr_supported)) |
746 | return 0; |
747 | |
748 | err = devl_params_register(devlink, params: mlx5_devlink_max_uc_list_params, |
749 | ARRAY_SIZE(mlx5_devlink_max_uc_list_params)); |
750 | if (err) |
751 | return err; |
752 | |
753 | value.vu32 = 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list); |
754 | devl_param_driverinit_value_set(devlink, |
755 | param_id: DEVLINK_PARAM_GENERIC_ID_MAX_MACS, |
756 | init_val: value); |
757 | return 0; |
758 | } |
759 | |
760 | static void |
761 | mlx5_devlink_max_uc_list_params_unregister(struct devlink *devlink) |
762 | { |
763 | struct mlx5_core_dev *dev = devlink_priv(devlink); |
764 | |
765 | if (!MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list_wr_supported)) |
766 | return; |
767 | |
768 | devl_params_unregister(devlink, params: mlx5_devlink_max_uc_list_params, |
769 | ARRAY_SIZE(mlx5_devlink_max_uc_list_params)); |
770 | } |
771 | |
772 | #define MLX5_TRAP_DROP(_id, _group_id) \ |
773 | DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ |
774 | DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ |
775 | DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT) |
776 | |
777 | static const struct devlink_trap mlx5_traps_arr[] = { |
778 | MLX5_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS), |
779 | MLX5_TRAP_DROP(DMAC_FILTER, L2_DROPS), |
780 | }; |
781 | |
782 | static const struct devlink_trap_group mlx5_trap_groups_arr[] = { |
783 | DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0), |
784 | }; |
785 | |
786 | int mlx5_devlink_traps_register(struct devlink *devlink) |
787 | { |
788 | struct mlx5_core_dev *core_dev = devlink_priv(devlink); |
789 | int err; |
790 | |
791 | err = devl_trap_groups_register(devlink, groups: mlx5_trap_groups_arr, |
792 | ARRAY_SIZE(mlx5_trap_groups_arr)); |
793 | if (err) |
794 | return err; |
795 | |
796 | err = devl_traps_register(devlink, traps: mlx5_traps_arr, ARRAY_SIZE(mlx5_traps_arr), |
797 | priv: &core_dev->priv); |
798 | if (err) |
799 | goto err_trap_group; |
800 | return 0; |
801 | |
802 | err_trap_group: |
803 | devl_trap_groups_unregister(devlink, groups: mlx5_trap_groups_arr, |
804 | ARRAY_SIZE(mlx5_trap_groups_arr)); |
805 | return err; |
806 | } |
807 | |
808 | void mlx5_devlink_traps_unregister(struct devlink *devlink) |
809 | { |
810 | devl_traps_unregister(devlink, traps: mlx5_traps_arr, ARRAY_SIZE(mlx5_traps_arr)); |
811 | devl_trap_groups_unregister(devlink, groups: mlx5_trap_groups_arr, |
812 | ARRAY_SIZE(mlx5_trap_groups_arr)); |
813 | } |
814 | |
815 | int mlx5_devlink_params_register(struct devlink *devlink) |
816 | { |
817 | int err; |
818 | |
819 | /* Here only the driver init params should be registered. |
820 | * Runtime params should be registered by the code which |
821 | * behaviour they configure. |
822 | */ |
823 | |
824 | err = devl_params_register(devlink, params: mlx5_devlink_params, |
825 | ARRAY_SIZE(mlx5_devlink_params)); |
826 | if (err) |
827 | return err; |
828 | |
829 | mlx5_devlink_set_params_init_values(devlink); |
830 | |
831 | err = mlx5_devlink_auxdev_params_register(devlink); |
832 | if (err) |
833 | goto auxdev_reg_err; |
834 | |
835 | err = mlx5_devlink_max_uc_list_params_register(devlink); |
836 | if (err) |
837 | goto max_uc_list_err; |
838 | |
839 | return 0; |
840 | |
841 | max_uc_list_err: |
842 | mlx5_devlink_auxdev_params_unregister(devlink); |
843 | auxdev_reg_err: |
844 | devl_params_unregister(devlink, params: mlx5_devlink_params, |
845 | ARRAY_SIZE(mlx5_devlink_params)); |
846 | return err; |
847 | } |
848 | |
849 | void mlx5_devlink_params_unregister(struct devlink *devlink) |
850 | { |
851 | mlx5_devlink_max_uc_list_params_unregister(devlink); |
852 | mlx5_devlink_auxdev_params_unregister(devlink); |
853 | devl_params_unregister(devlink, params: mlx5_devlink_params, |
854 | ARRAY_SIZE(mlx5_devlink_params)); |
855 | } |
856 | |