1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2016 Mellanox Technologies. All rights reserved. |
4 | * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> |
5 | */ |
6 | |
7 | #include <net/genetlink.h> |
8 | #include <net/sock.h> |
9 | #include "devl_internal.h" |
10 | |
11 | struct devlink_info_req { |
12 | struct sk_buff *msg; |
13 | void (*version_cb)(const char *version_name, |
14 | enum devlink_info_version_type version_type, |
15 | void *version_cb_priv); |
16 | void *version_cb_priv; |
17 | }; |
18 | |
19 | struct devlink_reload_combination { |
20 | enum devlink_reload_action action; |
21 | enum devlink_reload_limit limit; |
22 | }; |
23 | |
24 | static const struct devlink_reload_combination devlink_reload_invalid_combinations[] = { |
25 | { |
26 | /* can't reinitialize driver with no down time */ |
27 | .action = DEVLINK_RELOAD_ACTION_DRIVER_REINIT, |
28 | .limit = DEVLINK_RELOAD_LIMIT_NO_RESET, |
29 | }, |
30 | }; |
31 | |
32 | static bool |
33 | devlink_reload_combination_is_invalid(enum devlink_reload_action action, |
34 | enum devlink_reload_limit limit) |
35 | { |
36 | int i; |
37 | |
38 | for (i = 0; i < ARRAY_SIZE(devlink_reload_invalid_combinations); i++) |
39 | if (devlink_reload_invalid_combinations[i].action == action && |
40 | devlink_reload_invalid_combinations[i].limit == limit) |
41 | return true; |
42 | return false; |
43 | } |
44 | |
45 | static bool |
46 | devlink_reload_action_is_supported(struct devlink *devlink, enum devlink_reload_action action) |
47 | { |
48 | return test_bit(action, &devlink->ops->reload_actions); |
49 | } |
50 | |
51 | static bool |
52 | devlink_reload_limit_is_supported(struct devlink *devlink, enum devlink_reload_limit limit) |
53 | { |
54 | return test_bit(limit, &devlink->ops->reload_limits); |
55 | } |
56 | |
57 | static int devlink_reload_stat_put(struct sk_buff *msg, |
58 | enum devlink_reload_limit limit, u32 value) |
59 | { |
60 | struct nlattr *reload_stats_entry; |
61 | |
62 | reload_stats_entry = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_RELOAD_STATS_ENTRY); |
63 | if (!reload_stats_entry) |
64 | return -EMSGSIZE; |
65 | |
66 | if (nla_put_u8(skb: msg, attrtype: DEVLINK_ATTR_RELOAD_STATS_LIMIT, value: limit) || |
67 | nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_RELOAD_STATS_VALUE, value)) |
68 | goto nla_put_failure; |
69 | nla_nest_end(skb: msg, start: reload_stats_entry); |
70 | return 0; |
71 | |
72 | nla_put_failure: |
73 | nla_nest_cancel(skb: msg, start: reload_stats_entry); |
74 | return -EMSGSIZE; |
75 | } |
76 | |
77 | static int |
78 | devlink_reload_stats_put(struct sk_buff *msg, struct devlink *devlink, bool is_remote) |
79 | { |
80 | struct nlattr *reload_stats_attr, *act_info, *act_stats; |
81 | int i, j, stat_idx; |
82 | u32 value; |
83 | |
84 | if (!is_remote) |
85 | reload_stats_attr = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_RELOAD_STATS); |
86 | else |
87 | reload_stats_attr = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_REMOTE_RELOAD_STATS); |
88 | |
89 | if (!reload_stats_attr) |
90 | return -EMSGSIZE; |
91 | |
92 | for (i = 0; i <= DEVLINK_RELOAD_ACTION_MAX; i++) { |
93 | if ((!is_remote && |
94 | !devlink_reload_action_is_supported(devlink, action: i)) || |
95 | i == DEVLINK_RELOAD_ACTION_UNSPEC) |
96 | continue; |
97 | act_info = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_RELOAD_ACTION_INFO); |
98 | if (!act_info) |
99 | goto nla_put_failure; |
100 | |
101 | if (nla_put_u8(skb: msg, attrtype: DEVLINK_ATTR_RELOAD_ACTION, value: i)) |
102 | goto action_info_nest_cancel; |
103 | act_stats = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_RELOAD_ACTION_STATS); |
104 | if (!act_stats) |
105 | goto action_info_nest_cancel; |
106 | |
107 | for (j = 0; j <= DEVLINK_RELOAD_LIMIT_MAX; j++) { |
108 | /* Remote stats are shown even if not locally supported. |
109 | * Stats of actions with unspecified limit are shown |
110 | * though drivers don't need to register unspecified |
111 | * limit. |
112 | */ |
113 | if ((!is_remote && j != DEVLINK_RELOAD_LIMIT_UNSPEC && |
114 | !devlink_reload_limit_is_supported(devlink, limit: j)) || |
115 | devlink_reload_combination_is_invalid(action: i, limit: j)) |
116 | continue; |
117 | |
118 | stat_idx = j * __DEVLINK_RELOAD_ACTION_MAX + i; |
119 | if (!is_remote) |
120 | value = devlink->stats.reload_stats[stat_idx]; |
121 | else |
122 | value = devlink->stats.remote_reload_stats[stat_idx]; |
123 | if (devlink_reload_stat_put(msg, limit: j, value)) |
124 | goto action_stats_nest_cancel; |
125 | } |
126 | nla_nest_end(skb: msg, start: act_stats); |
127 | nla_nest_end(skb: msg, start: act_info); |
128 | } |
129 | nla_nest_end(skb: msg, start: reload_stats_attr); |
130 | return 0; |
131 | |
132 | action_stats_nest_cancel: |
133 | nla_nest_cancel(skb: msg, start: act_stats); |
134 | action_info_nest_cancel: |
135 | nla_nest_cancel(skb: msg, start: act_info); |
136 | nla_put_failure: |
137 | nla_nest_cancel(skb: msg, start: reload_stats_attr); |
138 | return -EMSGSIZE; |
139 | } |
140 | |
141 | static int devlink_nl_nested_fill(struct sk_buff *msg, struct devlink *devlink) |
142 | { |
143 | unsigned long rel_index; |
144 | void *unused; |
145 | int err; |
146 | |
147 | xa_for_each(&devlink->nested_rels, rel_index, unused) { |
148 | err = devlink_rel_devlink_handle_put(msg, devlink, |
149 | rel_index, |
150 | attrtype: DEVLINK_ATTR_NESTED_DEVLINK, |
151 | NULL); |
152 | if (err) |
153 | return err; |
154 | } |
155 | return 0; |
156 | } |
157 | |
158 | static int devlink_nl_fill(struct sk_buff *msg, struct devlink *devlink, |
159 | enum devlink_command cmd, u32 portid, |
160 | u32 seq, int flags) |
161 | { |
162 | struct nlattr *dev_stats; |
163 | void *hdr; |
164 | |
165 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
166 | if (!hdr) |
167 | return -EMSGSIZE; |
168 | |
169 | if (devlink_nl_put_handle(msg, devlink)) |
170 | goto nla_put_failure; |
171 | if (nla_put_u8(skb: msg, attrtype: DEVLINK_ATTR_RELOAD_FAILED, value: devlink->reload_failed)) |
172 | goto nla_put_failure; |
173 | |
174 | dev_stats = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_DEV_STATS); |
175 | if (!dev_stats) |
176 | goto nla_put_failure; |
177 | |
178 | if (devlink_reload_stats_put(msg, devlink, is_remote: false)) |
179 | goto dev_stats_nest_cancel; |
180 | if (devlink_reload_stats_put(msg, devlink, is_remote: true)) |
181 | goto dev_stats_nest_cancel; |
182 | |
183 | nla_nest_end(skb: msg, start: dev_stats); |
184 | |
185 | if (devlink_nl_nested_fill(msg, devlink)) |
186 | goto nla_put_failure; |
187 | |
188 | genlmsg_end(skb: msg, hdr); |
189 | return 0; |
190 | |
191 | dev_stats_nest_cancel: |
192 | nla_nest_cancel(skb: msg, start: dev_stats); |
193 | nla_put_failure: |
194 | genlmsg_cancel(skb: msg, hdr); |
195 | return -EMSGSIZE; |
196 | } |
197 | |
198 | static void devlink_notify(struct devlink *devlink, enum devlink_command cmd) |
199 | { |
200 | struct sk_buff *msg; |
201 | int err; |
202 | |
203 | WARN_ON(cmd != DEVLINK_CMD_NEW && cmd != DEVLINK_CMD_DEL); |
204 | WARN_ON(!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED)); |
205 | |
206 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
207 | if (!msg) |
208 | return; |
209 | |
210 | err = devlink_nl_fill(msg, devlink, cmd, portid: 0, seq: 0, flags: 0); |
211 | if (err) { |
212 | nlmsg_free(skb: msg); |
213 | return; |
214 | } |
215 | |
216 | genlmsg_multicast_netns(family: &devlink_nl_family, net: devlink_net(devlink), |
217 | skb: msg, portid: 0, group: DEVLINK_MCGRP_CONFIG, GFP_KERNEL); |
218 | } |
219 | |
220 | int devlink_nl_get_doit(struct sk_buff *skb, struct genl_info *info) |
221 | { |
222 | struct devlink *devlink = info->user_ptr[0]; |
223 | struct sk_buff *msg; |
224 | int err; |
225 | |
226 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
227 | if (!msg) |
228 | return -ENOMEM; |
229 | |
230 | err = devlink_nl_fill(msg, devlink, cmd: DEVLINK_CMD_NEW, |
231 | portid: info->snd_portid, seq: info->snd_seq, flags: 0); |
232 | if (err) { |
233 | nlmsg_free(skb: msg); |
234 | return err; |
235 | } |
236 | |
237 | return genlmsg_reply(skb: msg, info); |
238 | } |
239 | |
240 | static int |
241 | devlink_nl_get_dump_one(struct sk_buff *msg, struct devlink *devlink, |
242 | struct netlink_callback *cb, int flags) |
243 | { |
244 | return devlink_nl_fill(msg, devlink, cmd: DEVLINK_CMD_NEW, |
245 | NETLINK_CB(cb->skb).portid, |
246 | seq: cb->nlh->nlmsg_seq, flags); |
247 | } |
248 | |
249 | int devlink_nl_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb) |
250 | { |
251 | return devlink_nl_dumpit(msg, cb, dump_one: devlink_nl_get_dump_one); |
252 | } |
253 | |
254 | static void devlink_rel_notify_cb(struct devlink *devlink, u32 obj_index) |
255 | { |
256 | devlink_notify(devlink, cmd: DEVLINK_CMD_NEW); |
257 | } |
258 | |
259 | static void devlink_rel_cleanup_cb(struct devlink *devlink, u32 obj_index, |
260 | u32 rel_index) |
261 | { |
262 | xa_erase(&devlink->nested_rels, index: rel_index); |
263 | } |
264 | |
265 | int devl_nested_devlink_set(struct devlink *devlink, |
266 | struct devlink *nested_devlink) |
267 | { |
268 | u32 rel_index; |
269 | int err; |
270 | |
271 | err = devlink_rel_nested_in_add(rel_index: &rel_index, devlink_index: devlink->index, obj_index: 0, |
272 | notify_cb: devlink_rel_notify_cb, |
273 | cleanup_cb: devlink_rel_cleanup_cb, |
274 | devlink: nested_devlink); |
275 | if (err) |
276 | return err; |
277 | return xa_insert(xa: &devlink->nested_rels, index: rel_index, |
278 | entry: xa_mk_value(v: 0), GFP_KERNEL); |
279 | } |
280 | EXPORT_SYMBOL_GPL(devl_nested_devlink_set); |
281 | |
282 | void devlink_notify_register(struct devlink *devlink) |
283 | { |
284 | devlink_notify(devlink, cmd: DEVLINK_CMD_NEW); |
285 | devlink_linecards_notify_register(devlink); |
286 | devlink_ports_notify_register(devlink); |
287 | devlink_trap_policers_notify_register(devlink); |
288 | devlink_trap_groups_notify_register(devlink); |
289 | devlink_traps_notify_register(devlink); |
290 | devlink_rates_notify_register(devlink); |
291 | devlink_regions_notify_register(devlink); |
292 | devlink_params_notify_register(devlink); |
293 | } |
294 | |
295 | void devlink_notify_unregister(struct devlink *devlink) |
296 | { |
297 | devlink_params_notify_unregister(devlink); |
298 | devlink_regions_notify_unregister(devlink); |
299 | devlink_rates_notify_unregister(devlink); |
300 | devlink_traps_notify_unregister(devlink); |
301 | devlink_trap_groups_notify_unregister(devlink); |
302 | devlink_trap_policers_notify_unregister(devlink); |
303 | devlink_ports_notify_unregister(devlink); |
304 | devlink_linecards_notify_unregister(devlink); |
305 | devlink_notify(devlink, cmd: DEVLINK_CMD_DEL); |
306 | } |
307 | |
308 | static void devlink_reload_failed_set(struct devlink *devlink, |
309 | bool reload_failed) |
310 | { |
311 | if (devlink->reload_failed == reload_failed) |
312 | return; |
313 | devlink->reload_failed = reload_failed; |
314 | devlink_notify(devlink, cmd: DEVLINK_CMD_NEW); |
315 | } |
316 | |
317 | bool devlink_is_reload_failed(const struct devlink *devlink) |
318 | { |
319 | return devlink->reload_failed; |
320 | } |
321 | EXPORT_SYMBOL_GPL(devlink_is_reload_failed); |
322 | |
323 | static void |
324 | __devlink_reload_stats_update(struct devlink *devlink, u32 *reload_stats, |
325 | enum devlink_reload_limit limit, u32 actions_performed) |
326 | { |
327 | unsigned long actions = actions_performed; |
328 | int stat_idx; |
329 | int action; |
330 | |
331 | for_each_set_bit(action, &actions, __DEVLINK_RELOAD_ACTION_MAX) { |
332 | stat_idx = limit * __DEVLINK_RELOAD_ACTION_MAX + action; |
333 | reload_stats[stat_idx]++; |
334 | } |
335 | devlink_notify(devlink, cmd: DEVLINK_CMD_NEW); |
336 | } |
337 | |
338 | static void |
339 | devlink_reload_stats_update(struct devlink *devlink, enum devlink_reload_limit limit, |
340 | u32 actions_performed) |
341 | { |
342 | __devlink_reload_stats_update(devlink, reload_stats: devlink->stats.reload_stats, limit, |
343 | actions_performed); |
344 | } |
345 | |
346 | /** |
347 | * devlink_remote_reload_actions_performed - Update devlink on reload actions |
348 | * performed which are not a direct result of devlink reload call. |
349 | * |
350 | * This should be called by a driver after performing reload actions in case it was not |
351 | * a result of devlink reload call. For example fw_activate was performed as a result |
352 | * of devlink reload triggered fw_activate on another host. |
353 | * The motivation for this function is to keep data on reload actions performed on this |
354 | * function whether it was done due to direct devlink reload call or not. |
355 | * |
356 | * @devlink: devlink |
357 | * @limit: reload limit |
358 | * @actions_performed: bitmask of actions performed |
359 | */ |
360 | void devlink_remote_reload_actions_performed(struct devlink *devlink, |
361 | enum devlink_reload_limit limit, |
362 | u32 actions_performed) |
363 | { |
364 | if (WARN_ON(!actions_performed || |
365 | actions_performed & BIT(DEVLINK_RELOAD_ACTION_UNSPEC) || |
366 | actions_performed >= BIT(__DEVLINK_RELOAD_ACTION_MAX) || |
367 | limit > DEVLINK_RELOAD_LIMIT_MAX)) |
368 | return; |
369 | |
370 | __devlink_reload_stats_update(devlink, reload_stats: devlink->stats.remote_reload_stats, limit, |
371 | actions_performed); |
372 | } |
373 | EXPORT_SYMBOL_GPL(devlink_remote_reload_actions_performed); |
374 | |
375 | static struct net *devlink_netns_get(struct sk_buff *skb, |
376 | struct genl_info *info) |
377 | { |
378 | struct nlattr *netns_pid_attr = info->attrs[DEVLINK_ATTR_NETNS_PID]; |
379 | struct nlattr *netns_fd_attr = info->attrs[DEVLINK_ATTR_NETNS_FD]; |
380 | struct nlattr *netns_id_attr = info->attrs[DEVLINK_ATTR_NETNS_ID]; |
381 | struct net *net; |
382 | |
383 | if (!!netns_pid_attr + !!netns_fd_attr + !!netns_id_attr > 1) { |
384 | NL_SET_ERR_MSG(info->extack, "multiple netns identifying attributes specified" ); |
385 | return ERR_PTR(error: -EINVAL); |
386 | } |
387 | |
388 | if (netns_pid_attr) { |
389 | net = get_net_ns_by_pid(pid: nla_get_u32(nla: netns_pid_attr)); |
390 | } else if (netns_fd_attr) { |
391 | net = get_net_ns_by_fd(fd: nla_get_u32(nla: netns_fd_attr)); |
392 | } else if (netns_id_attr) { |
393 | net = get_net_ns_by_id(net: sock_net(sk: skb->sk), |
394 | id: nla_get_u32(nla: netns_id_attr)); |
395 | if (!net) |
396 | net = ERR_PTR(error: -EINVAL); |
397 | } else { |
398 | WARN_ON(1); |
399 | net = ERR_PTR(error: -EINVAL); |
400 | } |
401 | if (IS_ERR(ptr: net)) { |
402 | NL_SET_ERR_MSG(info->extack, "Unknown network namespace" ); |
403 | return ERR_PTR(error: -EINVAL); |
404 | } |
405 | if (!netlink_ns_capable(skb, ns: net->user_ns, CAP_NET_ADMIN)) { |
406 | put_net(net); |
407 | return ERR_PTR(error: -EPERM); |
408 | } |
409 | return net; |
410 | } |
411 | |
412 | static void devlink_reload_netns_change(struct devlink *devlink, |
413 | struct net *curr_net, |
414 | struct net *dest_net) |
415 | { |
416 | /* Userspace needs to be notified about devlink objects |
417 | * removed from original and entering new network namespace. |
418 | * The rest of the devlink objects are re-created during |
419 | * reload process so the notifications are generated separatelly. |
420 | */ |
421 | devlink_notify_unregister(devlink); |
422 | write_pnet(pnet: &devlink->_net, net: dest_net); |
423 | devlink_notify_register(devlink); |
424 | devlink_rel_nested_in_notify(devlink); |
425 | } |
426 | |
427 | int devlink_reload(struct devlink *devlink, struct net *dest_net, |
428 | enum devlink_reload_action action, |
429 | enum devlink_reload_limit limit, |
430 | u32 *actions_performed, struct netlink_ext_ack *extack) |
431 | { |
432 | u32 remote_reload_stats[DEVLINK_RELOAD_STATS_ARRAY_SIZE]; |
433 | struct net *curr_net; |
434 | int err; |
435 | |
436 | memcpy(remote_reload_stats, devlink->stats.remote_reload_stats, |
437 | sizeof(remote_reload_stats)); |
438 | |
439 | err = devlink->ops->reload_down(devlink, !!dest_net, action, limit, extack); |
440 | if (err) |
441 | return err; |
442 | |
443 | curr_net = devlink_net(devlink); |
444 | if (dest_net && !net_eq(net1: dest_net, net2: curr_net)) |
445 | devlink_reload_netns_change(devlink, curr_net, dest_net); |
446 | |
447 | if (action == DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
448 | devlink_params_driverinit_load_new(devlink); |
449 | |
450 | err = devlink->ops->reload_up(devlink, action, limit, actions_performed, extack); |
451 | devlink_reload_failed_set(devlink, reload_failed: !!err); |
452 | if (err) |
453 | return err; |
454 | |
455 | WARN_ON(!(*actions_performed & BIT(action))); |
456 | /* Catch driver on updating the remote action within devlink reload */ |
457 | WARN_ON(memcmp(remote_reload_stats, devlink->stats.remote_reload_stats, |
458 | sizeof(remote_reload_stats))); |
459 | devlink_reload_stats_update(devlink, limit, actions_performed: *actions_performed); |
460 | return 0; |
461 | } |
462 | |
463 | static int |
464 | devlink_nl_reload_actions_performed_snd(struct devlink *devlink, u32 actions_performed, |
465 | enum devlink_command cmd, struct genl_info *info) |
466 | { |
467 | struct sk_buff *msg; |
468 | void *hdr; |
469 | |
470 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
471 | if (!msg) |
472 | return -ENOMEM; |
473 | |
474 | hdr = genlmsg_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, family: &devlink_nl_family, flags: 0, cmd); |
475 | if (!hdr) |
476 | goto free_msg; |
477 | |
478 | if (devlink_nl_put_handle(msg, devlink)) |
479 | goto nla_put_failure; |
480 | |
481 | if (nla_put_bitfield32(skb: msg, attrtype: DEVLINK_ATTR_RELOAD_ACTIONS_PERFORMED, value: actions_performed, |
482 | selector: actions_performed)) |
483 | goto nla_put_failure; |
484 | genlmsg_end(skb: msg, hdr); |
485 | |
486 | return genlmsg_reply(skb: msg, info); |
487 | |
488 | nla_put_failure: |
489 | genlmsg_cancel(skb: msg, hdr); |
490 | free_msg: |
491 | nlmsg_free(skb: msg); |
492 | return -EMSGSIZE; |
493 | } |
494 | |
495 | int devlink_nl_reload_doit(struct sk_buff *skb, struct genl_info *info) |
496 | { |
497 | struct devlink *devlink = info->user_ptr[0]; |
498 | enum devlink_reload_action action; |
499 | enum devlink_reload_limit limit; |
500 | struct net *dest_net = NULL; |
501 | u32 actions_performed; |
502 | int err; |
503 | |
504 | err = devlink_resources_validate(devlink, NULL, info); |
505 | if (err) { |
506 | NL_SET_ERR_MSG(info->extack, "resources size validation failed" ); |
507 | return err; |
508 | } |
509 | |
510 | if (info->attrs[DEVLINK_ATTR_RELOAD_ACTION]) |
511 | action = nla_get_u8(nla: info->attrs[DEVLINK_ATTR_RELOAD_ACTION]); |
512 | else |
513 | action = DEVLINK_RELOAD_ACTION_DRIVER_REINIT; |
514 | |
515 | if (!devlink_reload_action_is_supported(devlink, action)) { |
516 | NL_SET_ERR_MSG(info->extack, "Requested reload action is not supported by the driver" ); |
517 | return -EOPNOTSUPP; |
518 | } |
519 | |
520 | limit = DEVLINK_RELOAD_LIMIT_UNSPEC; |
521 | if (info->attrs[DEVLINK_ATTR_RELOAD_LIMITS]) { |
522 | struct nla_bitfield32 limits; |
523 | u32 limits_selected; |
524 | |
525 | limits = nla_get_bitfield32(nla: info->attrs[DEVLINK_ATTR_RELOAD_LIMITS]); |
526 | limits_selected = limits.value & limits.selector; |
527 | if (!limits_selected) { |
528 | NL_SET_ERR_MSG(info->extack, "Invalid limit selected" ); |
529 | return -EINVAL; |
530 | } |
531 | for (limit = 0 ; limit <= DEVLINK_RELOAD_LIMIT_MAX ; limit++) |
532 | if (limits_selected & BIT(limit)) |
533 | break; |
534 | /* UAPI enables multiselection, but currently it is not used */ |
535 | if (limits_selected != BIT(limit)) { |
536 | NL_SET_ERR_MSG(info->extack, "Multiselection of limit is not supported" ); |
537 | return -EOPNOTSUPP; |
538 | } |
539 | if (!devlink_reload_limit_is_supported(devlink, limit)) { |
540 | NL_SET_ERR_MSG(info->extack, "Requested limit is not supported by the driver" ); |
541 | return -EOPNOTSUPP; |
542 | } |
543 | if (devlink_reload_combination_is_invalid(action, limit)) { |
544 | NL_SET_ERR_MSG(info->extack, "Requested limit is invalid for this action" ); |
545 | return -EINVAL; |
546 | } |
547 | } |
548 | if (info->attrs[DEVLINK_ATTR_NETNS_PID] || |
549 | info->attrs[DEVLINK_ATTR_NETNS_FD] || |
550 | info->attrs[DEVLINK_ATTR_NETNS_ID]) { |
551 | dest_net = devlink_netns_get(skb, info); |
552 | if (IS_ERR(ptr: dest_net)) |
553 | return PTR_ERR(ptr: dest_net); |
554 | if (!net_eq(net1: dest_net, net2: devlink_net(devlink)) && |
555 | action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT) { |
556 | NL_SET_ERR_MSG_MOD(info->extack, |
557 | "Changing namespace is only supported for reinit action" ); |
558 | return -EOPNOTSUPP; |
559 | } |
560 | } |
561 | |
562 | err = devlink_reload(devlink, dest_net, action, limit, actions_performed: &actions_performed, extack: info->extack); |
563 | |
564 | if (dest_net) |
565 | put_net(net: dest_net); |
566 | |
567 | if (err) |
568 | return err; |
569 | /* For backward compatibility generate reply only if attributes used by user */ |
570 | if (!info->attrs[DEVLINK_ATTR_RELOAD_ACTION] && !info->attrs[DEVLINK_ATTR_RELOAD_LIMITS]) |
571 | return 0; |
572 | |
573 | return devlink_nl_reload_actions_performed_snd(devlink, actions_performed, |
574 | cmd: DEVLINK_CMD_RELOAD, info); |
575 | } |
576 | |
577 | bool devlink_reload_actions_valid(const struct devlink_ops *ops) |
578 | { |
579 | const struct devlink_reload_combination *comb; |
580 | int i; |
581 | |
582 | if (!devlink_reload_supported(ops)) { |
583 | if (WARN_ON(ops->reload_actions)) |
584 | return false; |
585 | return true; |
586 | } |
587 | |
588 | if (WARN_ON(!ops->reload_actions || |
589 | ops->reload_actions & BIT(DEVLINK_RELOAD_ACTION_UNSPEC) || |
590 | ops->reload_actions >= BIT(__DEVLINK_RELOAD_ACTION_MAX))) |
591 | return false; |
592 | |
593 | if (WARN_ON(ops->reload_limits & BIT(DEVLINK_RELOAD_LIMIT_UNSPEC) || |
594 | ops->reload_limits >= BIT(__DEVLINK_RELOAD_LIMIT_MAX))) |
595 | return false; |
596 | |
597 | for (i = 0; i < ARRAY_SIZE(devlink_reload_invalid_combinations); i++) { |
598 | comb = &devlink_reload_invalid_combinations[i]; |
599 | if (ops->reload_actions == BIT(comb->action) && |
600 | ops->reload_limits == BIT(comb->limit)) |
601 | return false; |
602 | } |
603 | return true; |
604 | } |
605 | |
606 | static int devlink_nl_eswitch_fill(struct sk_buff *msg, struct devlink *devlink, |
607 | enum devlink_command cmd, u32 portid, |
608 | u32 seq, int flags) |
609 | { |
610 | const struct devlink_ops *ops = devlink->ops; |
611 | enum devlink_eswitch_encap_mode encap_mode; |
612 | u8 inline_mode; |
613 | void *hdr; |
614 | int err = 0; |
615 | u16 mode; |
616 | |
617 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
618 | if (!hdr) |
619 | return -EMSGSIZE; |
620 | |
621 | err = devlink_nl_put_handle(msg, devlink); |
622 | if (err) |
623 | goto nla_put_failure; |
624 | |
625 | if (ops->eswitch_mode_get) { |
626 | err = ops->eswitch_mode_get(devlink, &mode); |
627 | if (err) |
628 | goto nla_put_failure; |
629 | err = nla_put_u16(skb: msg, attrtype: DEVLINK_ATTR_ESWITCH_MODE, value: mode); |
630 | if (err) |
631 | goto nla_put_failure; |
632 | } |
633 | |
634 | if (ops->eswitch_inline_mode_get) { |
635 | err = ops->eswitch_inline_mode_get(devlink, &inline_mode); |
636 | if (err) |
637 | goto nla_put_failure; |
638 | err = nla_put_u8(skb: msg, attrtype: DEVLINK_ATTR_ESWITCH_INLINE_MODE, |
639 | value: inline_mode); |
640 | if (err) |
641 | goto nla_put_failure; |
642 | } |
643 | |
644 | if (ops->eswitch_encap_mode_get) { |
645 | err = ops->eswitch_encap_mode_get(devlink, &encap_mode); |
646 | if (err) |
647 | goto nla_put_failure; |
648 | err = nla_put_u8(skb: msg, attrtype: DEVLINK_ATTR_ESWITCH_ENCAP_MODE, value: encap_mode); |
649 | if (err) |
650 | goto nla_put_failure; |
651 | } |
652 | |
653 | genlmsg_end(skb: msg, hdr); |
654 | return 0; |
655 | |
656 | nla_put_failure: |
657 | genlmsg_cancel(skb: msg, hdr); |
658 | return err; |
659 | } |
660 | |
661 | int devlink_nl_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info) |
662 | { |
663 | struct devlink *devlink = info->user_ptr[0]; |
664 | struct sk_buff *msg; |
665 | int err; |
666 | |
667 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
668 | if (!msg) |
669 | return -ENOMEM; |
670 | |
671 | err = devlink_nl_eswitch_fill(msg, devlink, cmd: DEVLINK_CMD_ESWITCH_GET, |
672 | portid: info->snd_portid, seq: info->snd_seq, flags: 0); |
673 | |
674 | if (err) { |
675 | nlmsg_free(skb: msg); |
676 | return err; |
677 | } |
678 | |
679 | return genlmsg_reply(skb: msg, info); |
680 | } |
681 | |
682 | int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info) |
683 | { |
684 | struct devlink *devlink = info->user_ptr[0]; |
685 | const struct devlink_ops *ops = devlink->ops; |
686 | enum devlink_eswitch_encap_mode encap_mode; |
687 | u8 inline_mode; |
688 | int err = 0; |
689 | u16 mode; |
690 | |
691 | if (info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) { |
692 | if (!ops->eswitch_mode_set) |
693 | return -EOPNOTSUPP; |
694 | mode = nla_get_u16(nla: info->attrs[DEVLINK_ATTR_ESWITCH_MODE]); |
695 | err = devlink_rate_nodes_check(devlink, mode, extack: info->extack); |
696 | if (err) |
697 | return err; |
698 | err = ops->eswitch_mode_set(devlink, mode, info->extack); |
699 | if (err) |
700 | return err; |
701 | } |
702 | |
703 | if (info->attrs[DEVLINK_ATTR_ESWITCH_INLINE_MODE]) { |
704 | if (!ops->eswitch_inline_mode_set) |
705 | return -EOPNOTSUPP; |
706 | inline_mode = nla_get_u8(nla: info->attrs[DEVLINK_ATTR_ESWITCH_INLINE_MODE]); |
707 | err = ops->eswitch_inline_mode_set(devlink, inline_mode, |
708 | info->extack); |
709 | if (err) |
710 | return err; |
711 | } |
712 | |
713 | if (info->attrs[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]) { |
714 | if (!ops->eswitch_encap_mode_set) |
715 | return -EOPNOTSUPP; |
716 | encap_mode = nla_get_u8(nla: info->attrs[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]); |
717 | err = ops->eswitch_encap_mode_set(devlink, encap_mode, |
718 | info->extack); |
719 | if (err) |
720 | return err; |
721 | } |
722 | |
723 | return 0; |
724 | } |
725 | |
726 | int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn) |
727 | { |
728 | if (!req->msg) |
729 | return 0; |
730 | return nla_put_string(skb: req->msg, attrtype: DEVLINK_ATTR_INFO_SERIAL_NUMBER, str: sn); |
731 | } |
732 | EXPORT_SYMBOL_GPL(devlink_info_serial_number_put); |
733 | |
734 | int devlink_info_board_serial_number_put(struct devlink_info_req *req, |
735 | const char *bsn) |
736 | { |
737 | if (!req->msg) |
738 | return 0; |
739 | return nla_put_string(skb: req->msg, attrtype: DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER, |
740 | str: bsn); |
741 | } |
742 | EXPORT_SYMBOL_GPL(devlink_info_board_serial_number_put); |
743 | |
744 | static int devlink_info_version_put(struct devlink_info_req *req, int attr, |
745 | const char *version_name, |
746 | const char *version_value, |
747 | enum devlink_info_version_type version_type) |
748 | { |
749 | struct nlattr *nest; |
750 | int err; |
751 | |
752 | if (req->version_cb) |
753 | req->version_cb(version_name, version_type, |
754 | req->version_cb_priv); |
755 | |
756 | if (!req->msg) |
757 | return 0; |
758 | |
759 | nest = nla_nest_start_noflag(skb: req->msg, attrtype: attr); |
760 | if (!nest) |
761 | return -EMSGSIZE; |
762 | |
763 | err = nla_put_string(skb: req->msg, attrtype: DEVLINK_ATTR_INFO_VERSION_NAME, |
764 | str: version_name); |
765 | if (err) |
766 | goto nla_put_failure; |
767 | |
768 | err = nla_put_string(skb: req->msg, attrtype: DEVLINK_ATTR_INFO_VERSION_VALUE, |
769 | str: version_value); |
770 | if (err) |
771 | goto nla_put_failure; |
772 | |
773 | nla_nest_end(skb: req->msg, start: nest); |
774 | |
775 | return 0; |
776 | |
777 | nla_put_failure: |
778 | nla_nest_cancel(skb: req->msg, start: nest); |
779 | return err; |
780 | } |
781 | |
782 | int devlink_info_version_fixed_put(struct devlink_info_req *req, |
783 | const char *version_name, |
784 | const char *version_value) |
785 | { |
786 | return devlink_info_version_put(req, attr: DEVLINK_ATTR_INFO_VERSION_FIXED, |
787 | version_name, version_value, |
788 | version_type: DEVLINK_INFO_VERSION_TYPE_NONE); |
789 | } |
790 | EXPORT_SYMBOL_GPL(devlink_info_version_fixed_put); |
791 | |
792 | int devlink_info_version_stored_put(struct devlink_info_req *req, |
793 | const char *version_name, |
794 | const char *version_value) |
795 | { |
796 | return devlink_info_version_put(req, attr: DEVLINK_ATTR_INFO_VERSION_STORED, |
797 | version_name, version_value, |
798 | version_type: DEVLINK_INFO_VERSION_TYPE_NONE); |
799 | } |
800 | EXPORT_SYMBOL_GPL(devlink_info_version_stored_put); |
801 | |
802 | int devlink_info_version_stored_put_ext(struct devlink_info_req *req, |
803 | const char *version_name, |
804 | const char *version_value, |
805 | enum devlink_info_version_type version_type) |
806 | { |
807 | return devlink_info_version_put(req, attr: DEVLINK_ATTR_INFO_VERSION_STORED, |
808 | version_name, version_value, |
809 | version_type); |
810 | } |
811 | EXPORT_SYMBOL_GPL(devlink_info_version_stored_put_ext); |
812 | |
813 | int devlink_info_version_running_put(struct devlink_info_req *req, |
814 | const char *version_name, |
815 | const char *version_value) |
816 | { |
817 | return devlink_info_version_put(req, attr: DEVLINK_ATTR_INFO_VERSION_RUNNING, |
818 | version_name, version_value, |
819 | version_type: DEVLINK_INFO_VERSION_TYPE_NONE); |
820 | } |
821 | EXPORT_SYMBOL_GPL(devlink_info_version_running_put); |
822 | |
823 | int devlink_info_version_running_put_ext(struct devlink_info_req *req, |
824 | const char *version_name, |
825 | const char *version_value, |
826 | enum devlink_info_version_type version_type) |
827 | { |
828 | return devlink_info_version_put(req, attr: DEVLINK_ATTR_INFO_VERSION_RUNNING, |
829 | version_name, version_value, |
830 | version_type); |
831 | } |
832 | EXPORT_SYMBOL_GPL(devlink_info_version_running_put_ext); |
833 | |
834 | static int devlink_nl_driver_info_get(struct device_driver *drv, |
835 | struct devlink_info_req *req) |
836 | { |
837 | if (!drv) |
838 | return 0; |
839 | |
840 | if (drv->name[0]) |
841 | return nla_put_string(skb: req->msg, attrtype: DEVLINK_ATTR_INFO_DRIVER_NAME, |
842 | str: drv->name); |
843 | |
844 | return 0; |
845 | } |
846 | |
847 | static int |
848 | devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink, |
849 | enum devlink_command cmd, u32 portid, |
850 | u32 seq, int flags, struct netlink_ext_ack *extack) |
851 | { |
852 | struct device *dev = devlink_to_dev(devlink); |
853 | struct devlink_info_req req = {}; |
854 | void *hdr; |
855 | int err; |
856 | |
857 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
858 | if (!hdr) |
859 | return -EMSGSIZE; |
860 | |
861 | err = -EMSGSIZE; |
862 | if (devlink_nl_put_handle(msg, devlink)) |
863 | goto err_cancel_msg; |
864 | |
865 | req.msg = msg; |
866 | if (devlink->ops->info_get) { |
867 | err = devlink->ops->info_get(devlink, &req, extack); |
868 | if (err) |
869 | goto err_cancel_msg; |
870 | } |
871 | |
872 | err = devlink_nl_driver_info_get(drv: dev->driver, req: &req); |
873 | if (err) |
874 | goto err_cancel_msg; |
875 | |
876 | genlmsg_end(skb: msg, hdr); |
877 | return 0; |
878 | |
879 | err_cancel_msg: |
880 | genlmsg_cancel(skb: msg, hdr); |
881 | return err; |
882 | } |
883 | |
884 | int devlink_nl_info_get_doit(struct sk_buff *skb, struct genl_info *info) |
885 | { |
886 | struct devlink *devlink = info->user_ptr[0]; |
887 | struct sk_buff *msg; |
888 | int err; |
889 | |
890 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
891 | if (!msg) |
892 | return -ENOMEM; |
893 | |
894 | err = devlink_nl_info_fill(msg, devlink, cmd: DEVLINK_CMD_INFO_GET, |
895 | portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
896 | extack: info->extack); |
897 | if (err) { |
898 | nlmsg_free(skb: msg); |
899 | return err; |
900 | } |
901 | |
902 | return genlmsg_reply(skb: msg, info); |
903 | } |
904 | |
905 | static int |
906 | devlink_nl_info_get_dump_one(struct sk_buff *msg, struct devlink *devlink, |
907 | struct netlink_callback *cb, int flags) |
908 | { |
909 | int err; |
910 | |
911 | err = devlink_nl_info_fill(msg, devlink, cmd: DEVLINK_CMD_INFO_GET, |
912 | NETLINK_CB(cb->skb).portid, |
913 | seq: cb->nlh->nlmsg_seq, flags, |
914 | extack: cb->extack); |
915 | if (err == -EOPNOTSUPP) |
916 | err = 0; |
917 | return err; |
918 | } |
919 | |
920 | int devlink_nl_info_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb) |
921 | { |
922 | return devlink_nl_dumpit(msg, cb, dump_one: devlink_nl_info_get_dump_one); |
923 | } |
924 | |
925 | static int devlink_nl_flash_update_fill(struct sk_buff *msg, |
926 | struct devlink *devlink, |
927 | enum devlink_command cmd, |
928 | struct devlink_flash_notify *params) |
929 | { |
930 | void *hdr; |
931 | |
932 | hdr = genlmsg_put(skb: msg, portid: 0, seq: 0, family: &devlink_nl_family, flags: 0, cmd); |
933 | if (!hdr) |
934 | return -EMSGSIZE; |
935 | |
936 | if (devlink_nl_put_handle(msg, devlink)) |
937 | goto nla_put_failure; |
938 | |
939 | if (cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS) |
940 | goto out; |
941 | |
942 | if (params->status_msg && |
943 | nla_put_string(skb: msg, attrtype: DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG, |
944 | str: params->status_msg)) |
945 | goto nla_put_failure; |
946 | if (params->component && |
947 | nla_put_string(skb: msg, attrtype: DEVLINK_ATTR_FLASH_UPDATE_COMPONENT, |
948 | str: params->component)) |
949 | goto nla_put_failure; |
950 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE, |
951 | value: params->done, padattr: DEVLINK_ATTR_PAD)) |
952 | goto nla_put_failure; |
953 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL, |
954 | value: params->total, padattr: DEVLINK_ATTR_PAD)) |
955 | goto nla_put_failure; |
956 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_FLASH_UPDATE_STATUS_TIMEOUT, |
957 | value: params->timeout, padattr: DEVLINK_ATTR_PAD)) |
958 | goto nla_put_failure; |
959 | |
960 | out: |
961 | genlmsg_end(skb: msg, hdr); |
962 | return 0; |
963 | |
964 | nla_put_failure: |
965 | genlmsg_cancel(skb: msg, hdr); |
966 | return -EMSGSIZE; |
967 | } |
968 | |
969 | static void __devlink_flash_update_notify(struct devlink *devlink, |
970 | enum devlink_command cmd, |
971 | struct devlink_flash_notify *params) |
972 | { |
973 | struct sk_buff *msg; |
974 | int err; |
975 | |
976 | WARN_ON(cmd != DEVLINK_CMD_FLASH_UPDATE && |
977 | cmd != DEVLINK_CMD_FLASH_UPDATE_END && |
978 | cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS); |
979 | |
980 | if (!xa_get_mark(&devlinks, index: devlink->index, DEVLINK_REGISTERED)) |
981 | return; |
982 | |
983 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
984 | if (!msg) |
985 | return; |
986 | |
987 | err = devlink_nl_flash_update_fill(msg, devlink, cmd, params); |
988 | if (err) |
989 | goto out_free_msg; |
990 | |
991 | genlmsg_multicast_netns(family: &devlink_nl_family, net: devlink_net(devlink), |
992 | skb: msg, portid: 0, group: DEVLINK_MCGRP_CONFIG, GFP_KERNEL); |
993 | return; |
994 | |
995 | out_free_msg: |
996 | nlmsg_free(skb: msg); |
997 | } |
998 | |
999 | static void devlink_flash_update_begin_notify(struct devlink *devlink) |
1000 | { |
1001 | struct devlink_flash_notify params = {}; |
1002 | |
1003 | __devlink_flash_update_notify(devlink, |
1004 | cmd: DEVLINK_CMD_FLASH_UPDATE, |
1005 | params: ¶ms); |
1006 | } |
1007 | |
1008 | static void devlink_flash_update_end_notify(struct devlink *devlink) |
1009 | { |
1010 | struct devlink_flash_notify params = {}; |
1011 | |
1012 | __devlink_flash_update_notify(devlink, |
1013 | cmd: DEVLINK_CMD_FLASH_UPDATE_END, |
1014 | params: ¶ms); |
1015 | } |
1016 | |
1017 | void devlink_flash_update_status_notify(struct devlink *devlink, |
1018 | const char *status_msg, |
1019 | const char *component, |
1020 | unsigned long done, |
1021 | unsigned long total) |
1022 | { |
1023 | struct devlink_flash_notify params = { |
1024 | .status_msg = status_msg, |
1025 | .component = component, |
1026 | .done = done, |
1027 | .total = total, |
1028 | }; |
1029 | |
1030 | __devlink_flash_update_notify(devlink, |
1031 | cmd: DEVLINK_CMD_FLASH_UPDATE_STATUS, |
1032 | params: ¶ms); |
1033 | } |
1034 | EXPORT_SYMBOL_GPL(devlink_flash_update_status_notify); |
1035 | |
1036 | void devlink_flash_update_timeout_notify(struct devlink *devlink, |
1037 | const char *status_msg, |
1038 | const char *component, |
1039 | unsigned long timeout) |
1040 | { |
1041 | struct devlink_flash_notify params = { |
1042 | .status_msg = status_msg, |
1043 | .component = component, |
1044 | .timeout = timeout, |
1045 | }; |
1046 | |
1047 | __devlink_flash_update_notify(devlink, |
1048 | cmd: DEVLINK_CMD_FLASH_UPDATE_STATUS, |
1049 | params: ¶ms); |
1050 | } |
1051 | EXPORT_SYMBOL_GPL(devlink_flash_update_timeout_notify); |
1052 | |
1053 | struct devlink_flash_component_lookup_ctx { |
1054 | const char *lookup_name; |
1055 | bool lookup_name_found; |
1056 | }; |
1057 | |
1058 | static void |
1059 | devlink_flash_component_lookup_cb(const char *version_name, |
1060 | enum devlink_info_version_type version_type, |
1061 | void *version_cb_priv) |
1062 | { |
1063 | struct devlink_flash_component_lookup_ctx *lookup_ctx = version_cb_priv; |
1064 | |
1065 | if (version_type != DEVLINK_INFO_VERSION_TYPE_COMPONENT || |
1066 | lookup_ctx->lookup_name_found) |
1067 | return; |
1068 | |
1069 | lookup_ctx->lookup_name_found = |
1070 | !strcmp(lookup_ctx->lookup_name, version_name); |
1071 | } |
1072 | |
1073 | static int devlink_flash_component_get(struct devlink *devlink, |
1074 | struct nlattr *nla_component, |
1075 | const char **p_component, |
1076 | struct netlink_ext_ack *extack) |
1077 | { |
1078 | struct devlink_flash_component_lookup_ctx lookup_ctx = {}; |
1079 | struct devlink_info_req req = {}; |
1080 | const char *component; |
1081 | int ret; |
1082 | |
1083 | if (!nla_component) |
1084 | return 0; |
1085 | |
1086 | component = nla_data(nla: nla_component); |
1087 | |
1088 | if (!devlink->ops->info_get) { |
1089 | NL_SET_ERR_MSG_ATTR(extack, nla_component, |
1090 | "component update is not supported by this device" ); |
1091 | return -EOPNOTSUPP; |
1092 | } |
1093 | |
1094 | lookup_ctx.lookup_name = component; |
1095 | req.version_cb = devlink_flash_component_lookup_cb; |
1096 | req.version_cb_priv = &lookup_ctx; |
1097 | |
1098 | ret = devlink->ops->info_get(devlink, &req, NULL); |
1099 | if (ret) |
1100 | return ret; |
1101 | |
1102 | if (!lookup_ctx.lookup_name_found) { |
1103 | NL_SET_ERR_MSG_ATTR(extack, nla_component, |
1104 | "selected component is not supported by this device" ); |
1105 | return -EINVAL; |
1106 | } |
1107 | *p_component = component; |
1108 | return 0; |
1109 | } |
1110 | |
1111 | int devlink_nl_flash_update_doit(struct sk_buff *skb, struct genl_info *info) |
1112 | { |
1113 | struct nlattr *nla_overwrite_mask, *nla_file_name; |
1114 | struct devlink_flash_update_params params = {}; |
1115 | struct devlink *devlink = info->user_ptr[0]; |
1116 | const char *file_name; |
1117 | u32 supported_params; |
1118 | int ret; |
1119 | |
1120 | if (!devlink->ops->flash_update) |
1121 | return -EOPNOTSUPP; |
1122 | |
1123 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME)) |
1124 | return -EINVAL; |
1125 | |
1126 | ret = devlink_flash_component_get(devlink, |
1127 | nla_component: info->attrs[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT], |
1128 | p_component: ¶ms.component, extack: info->extack); |
1129 | if (ret) |
1130 | return ret; |
1131 | |
1132 | supported_params = devlink->ops->supported_flash_update_params; |
1133 | |
1134 | nla_overwrite_mask = info->attrs[DEVLINK_ATTR_FLASH_UPDATE_OVERWRITE_MASK]; |
1135 | if (nla_overwrite_mask) { |
1136 | struct nla_bitfield32 sections; |
1137 | |
1138 | if (!(supported_params & DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK)) { |
1139 | NL_SET_ERR_MSG_ATTR(info->extack, nla_overwrite_mask, |
1140 | "overwrite settings are not supported by this device" ); |
1141 | return -EOPNOTSUPP; |
1142 | } |
1143 | sections = nla_get_bitfield32(nla: nla_overwrite_mask); |
1144 | params.overwrite_mask = sections.value & sections.selector; |
1145 | } |
1146 | |
1147 | nla_file_name = info->attrs[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME]; |
1148 | file_name = nla_data(nla: nla_file_name); |
1149 | ret = request_firmware(fw: ¶ms.fw, name: file_name, device: devlink->dev); |
1150 | if (ret) { |
1151 | NL_SET_ERR_MSG_ATTR(info->extack, nla_file_name, |
1152 | "failed to locate the requested firmware file" ); |
1153 | return ret; |
1154 | } |
1155 | |
1156 | devlink_flash_update_begin_notify(devlink); |
1157 | ret = devlink->ops->flash_update(devlink, ¶ms, info->extack); |
1158 | devlink_flash_update_end_notify(devlink); |
1159 | |
1160 | release_firmware(fw: params.fw); |
1161 | |
1162 | return ret; |
1163 | } |
1164 | |
1165 | static void __devlink_compat_running_version(struct devlink *devlink, |
1166 | char *buf, size_t len) |
1167 | { |
1168 | struct devlink_info_req req = {}; |
1169 | const struct nlattr *nlattr; |
1170 | struct sk_buff *msg; |
1171 | int rem, err; |
1172 | |
1173 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1174 | if (!msg) |
1175 | return; |
1176 | |
1177 | req.msg = msg; |
1178 | err = devlink->ops->info_get(devlink, &req, NULL); |
1179 | if (err) |
1180 | goto free_msg; |
1181 | |
1182 | nla_for_each_attr(nlattr, (void *)msg->data, msg->len, rem) { |
1183 | const struct nlattr *kv; |
1184 | int rem_kv; |
1185 | |
1186 | if (nla_type(nla: nlattr) != DEVLINK_ATTR_INFO_VERSION_RUNNING) |
1187 | continue; |
1188 | |
1189 | nla_for_each_nested(kv, nlattr, rem_kv) { |
1190 | if (nla_type(nla: kv) != DEVLINK_ATTR_INFO_VERSION_VALUE) |
1191 | continue; |
1192 | |
1193 | strlcat(p: buf, q: nla_data(nla: kv), avail: len); |
1194 | strlcat(p: buf, q: " " , avail: len); |
1195 | } |
1196 | } |
1197 | free_msg: |
1198 | nlmsg_free(skb: msg); |
1199 | } |
1200 | |
1201 | void devlink_compat_running_version(struct devlink *devlink, |
1202 | char *buf, size_t len) |
1203 | { |
1204 | if (!devlink->ops->info_get) |
1205 | return; |
1206 | |
1207 | devl_lock(devlink); |
1208 | if (devl_is_registered(devlink)) |
1209 | __devlink_compat_running_version(devlink, buf, len); |
1210 | devl_unlock(devlink); |
1211 | } |
1212 | |
1213 | int devlink_compat_flash_update(struct devlink *devlink, const char *file_name) |
1214 | { |
1215 | struct devlink_flash_update_params params = {}; |
1216 | int ret; |
1217 | |
1218 | devl_lock(devlink); |
1219 | if (!devl_is_registered(devlink)) { |
1220 | ret = -ENODEV; |
1221 | goto out_unlock; |
1222 | } |
1223 | |
1224 | if (!devlink->ops->flash_update) { |
1225 | ret = -EOPNOTSUPP; |
1226 | goto out_unlock; |
1227 | } |
1228 | |
1229 | ret = request_firmware(fw: ¶ms.fw, name: file_name, device: devlink->dev); |
1230 | if (ret) |
1231 | goto out_unlock; |
1232 | |
1233 | devlink_flash_update_begin_notify(devlink); |
1234 | ret = devlink->ops->flash_update(devlink, ¶ms, NULL); |
1235 | devlink_flash_update_end_notify(devlink); |
1236 | |
1237 | release_firmware(fw: params.fw); |
1238 | out_unlock: |
1239 | devl_unlock(devlink); |
1240 | |
1241 | return ret; |
1242 | } |
1243 | |
1244 | static int |
1245 | devlink_nl_selftests_fill(struct sk_buff *msg, struct devlink *devlink, |
1246 | u32 portid, u32 seq, int flags, |
1247 | struct netlink_ext_ack *extack) |
1248 | { |
1249 | struct nlattr *selftests; |
1250 | void *hdr; |
1251 | int err; |
1252 | int i; |
1253 | |
1254 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, |
1255 | cmd: DEVLINK_CMD_SELFTESTS_GET); |
1256 | if (!hdr) |
1257 | return -EMSGSIZE; |
1258 | |
1259 | err = -EMSGSIZE; |
1260 | if (devlink_nl_put_handle(msg, devlink)) |
1261 | goto err_cancel_msg; |
1262 | |
1263 | selftests = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_SELFTESTS); |
1264 | if (!selftests) |
1265 | goto err_cancel_msg; |
1266 | |
1267 | for (i = DEVLINK_ATTR_SELFTEST_ID_UNSPEC + 1; |
1268 | i <= DEVLINK_ATTR_SELFTEST_ID_MAX; i++) { |
1269 | if (devlink->ops->selftest_check(devlink, i, extack)) { |
1270 | err = nla_put_flag(skb: msg, attrtype: i); |
1271 | if (err) |
1272 | goto err_cancel_msg; |
1273 | } |
1274 | } |
1275 | |
1276 | nla_nest_end(skb: msg, start: selftests); |
1277 | genlmsg_end(skb: msg, hdr); |
1278 | return 0; |
1279 | |
1280 | err_cancel_msg: |
1281 | genlmsg_cancel(skb: msg, hdr); |
1282 | return err; |
1283 | } |
1284 | |
1285 | int devlink_nl_selftests_get_doit(struct sk_buff *skb, struct genl_info *info) |
1286 | { |
1287 | struct devlink *devlink = info->user_ptr[0]; |
1288 | struct sk_buff *msg; |
1289 | int err; |
1290 | |
1291 | if (!devlink->ops->selftest_check) |
1292 | return -EOPNOTSUPP; |
1293 | |
1294 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1295 | if (!msg) |
1296 | return -ENOMEM; |
1297 | |
1298 | err = devlink_nl_selftests_fill(msg, devlink, portid: info->snd_portid, |
1299 | seq: info->snd_seq, flags: 0, extack: info->extack); |
1300 | if (err) { |
1301 | nlmsg_free(skb: msg); |
1302 | return err; |
1303 | } |
1304 | |
1305 | return genlmsg_reply(skb: msg, info); |
1306 | } |
1307 | |
1308 | static int devlink_nl_selftests_get_dump_one(struct sk_buff *msg, |
1309 | struct devlink *devlink, |
1310 | struct netlink_callback *cb, |
1311 | int flags) |
1312 | { |
1313 | if (!devlink->ops->selftest_check) |
1314 | return 0; |
1315 | |
1316 | return devlink_nl_selftests_fill(msg, devlink, |
1317 | NETLINK_CB(cb->skb).portid, |
1318 | seq: cb->nlh->nlmsg_seq, flags, |
1319 | extack: cb->extack); |
1320 | } |
1321 | |
1322 | int devlink_nl_selftests_get_dumpit(struct sk_buff *skb, |
1323 | struct netlink_callback *cb) |
1324 | { |
1325 | return devlink_nl_dumpit(msg: skb, cb, dump_one: devlink_nl_selftests_get_dump_one); |
1326 | } |
1327 | |
1328 | static int devlink_selftest_result_put(struct sk_buff *skb, unsigned int id, |
1329 | enum devlink_selftest_status test_status) |
1330 | { |
1331 | struct nlattr *result_attr; |
1332 | |
1333 | result_attr = nla_nest_start(skb, attrtype: DEVLINK_ATTR_SELFTEST_RESULT); |
1334 | if (!result_attr) |
1335 | return -EMSGSIZE; |
1336 | |
1337 | if (nla_put_u32(skb, attrtype: DEVLINK_ATTR_SELFTEST_RESULT_ID, value: id) || |
1338 | nla_put_u8(skb, attrtype: DEVLINK_ATTR_SELFTEST_RESULT_STATUS, |
1339 | value: test_status)) |
1340 | goto nla_put_failure; |
1341 | |
1342 | nla_nest_end(skb, start: result_attr); |
1343 | return 0; |
1344 | |
1345 | nla_put_failure: |
1346 | nla_nest_cancel(skb, start: result_attr); |
1347 | return -EMSGSIZE; |
1348 | } |
1349 | |
1350 | static const struct nla_policy devlink_selftest_nl_policy[DEVLINK_ATTR_SELFTEST_ID_MAX + 1] = { |
1351 | [DEVLINK_ATTR_SELFTEST_ID_FLASH] = { .type = NLA_FLAG }, |
1352 | }; |
1353 | |
1354 | int devlink_nl_selftests_run_doit(struct sk_buff *skb, struct genl_info *info) |
1355 | { |
1356 | struct nlattr *tb[DEVLINK_ATTR_SELFTEST_ID_MAX + 1]; |
1357 | struct devlink *devlink = info->user_ptr[0]; |
1358 | struct nlattr *attrs, *selftests; |
1359 | struct sk_buff *msg; |
1360 | void *hdr; |
1361 | int err; |
1362 | int i; |
1363 | |
1364 | if (!devlink->ops->selftest_run || !devlink->ops->selftest_check) |
1365 | return -EOPNOTSUPP; |
1366 | |
1367 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SELFTESTS)) |
1368 | return -EINVAL; |
1369 | |
1370 | attrs = info->attrs[DEVLINK_ATTR_SELFTESTS]; |
1371 | |
1372 | err = nla_parse_nested(tb, maxtype: DEVLINK_ATTR_SELFTEST_ID_MAX, nla: attrs, |
1373 | policy: devlink_selftest_nl_policy, extack: info->extack); |
1374 | if (err < 0) |
1375 | return err; |
1376 | |
1377 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1378 | if (!msg) |
1379 | return -ENOMEM; |
1380 | |
1381 | err = -EMSGSIZE; |
1382 | hdr = genlmsg_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, |
1383 | family: &devlink_nl_family, flags: 0, cmd: DEVLINK_CMD_SELFTESTS_RUN); |
1384 | if (!hdr) |
1385 | goto free_msg; |
1386 | |
1387 | if (devlink_nl_put_handle(msg, devlink)) |
1388 | goto genlmsg_cancel; |
1389 | |
1390 | selftests = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_SELFTESTS); |
1391 | if (!selftests) |
1392 | goto genlmsg_cancel; |
1393 | |
1394 | for (i = DEVLINK_ATTR_SELFTEST_ID_UNSPEC + 1; |
1395 | i <= DEVLINK_ATTR_SELFTEST_ID_MAX; i++) { |
1396 | enum devlink_selftest_status test_status; |
1397 | |
1398 | if (nla_get_flag(nla: tb[i])) { |
1399 | if (!devlink->ops->selftest_check(devlink, i, |
1400 | info->extack)) { |
1401 | if (devlink_selftest_result_put(skb: msg, id: i, |
1402 | test_status: DEVLINK_SELFTEST_STATUS_SKIP)) |
1403 | goto selftests_nest_cancel; |
1404 | continue; |
1405 | } |
1406 | |
1407 | test_status = devlink->ops->selftest_run(devlink, i, |
1408 | info->extack); |
1409 | if (devlink_selftest_result_put(skb: msg, id: i, test_status)) |
1410 | goto selftests_nest_cancel; |
1411 | } |
1412 | } |
1413 | |
1414 | nla_nest_end(skb: msg, start: selftests); |
1415 | genlmsg_end(skb: msg, hdr); |
1416 | return genlmsg_reply(skb: msg, info); |
1417 | |
1418 | selftests_nest_cancel: |
1419 | nla_nest_cancel(skb: msg, start: selftests); |
1420 | genlmsg_cancel: |
1421 | genlmsg_cancel(skb: msg, hdr); |
1422 | free_msg: |
1423 | nlmsg_free(skb: msg); |
1424 | return err; |
1425 | } |
1426 | |