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