Commit | Line | Data |
---|---|---|
830c41e1 JP |
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 "devl_internal.h" | |
8 | ||
9 | static const struct devlink_param devlink_param_generic[] = { | |
10 | { | |
11 | .id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET, | |
12 | .name = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_NAME, | |
13 | .type = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_TYPE, | |
14 | }, | |
15 | { | |
16 | .id = DEVLINK_PARAM_GENERIC_ID_MAX_MACS, | |
17 | .name = DEVLINK_PARAM_GENERIC_MAX_MACS_NAME, | |
18 | .type = DEVLINK_PARAM_GENERIC_MAX_MACS_TYPE, | |
19 | }, | |
20 | { | |
21 | .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_SRIOV, | |
22 | .name = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_NAME, | |
23 | .type = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_TYPE, | |
24 | }, | |
25 | { | |
26 | .id = DEVLINK_PARAM_GENERIC_ID_REGION_SNAPSHOT, | |
27 | .name = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_NAME, | |
28 | .type = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_TYPE, | |
29 | }, | |
30 | { | |
31 | .id = DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI, | |
32 | .name = DEVLINK_PARAM_GENERIC_IGNORE_ARI_NAME, | |
33 | .type = DEVLINK_PARAM_GENERIC_IGNORE_ARI_TYPE, | |
34 | }, | |
35 | { | |
36 | .id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX, | |
37 | .name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_NAME, | |
38 | .type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_TYPE, | |
39 | }, | |
40 | { | |
41 | .id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN, | |
42 | .name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME, | |
43 | .type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE, | |
44 | }, | |
45 | { | |
46 | .id = DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY, | |
47 | .name = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME, | |
48 | .type = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE, | |
49 | }, | |
50 | { | |
51 | .id = DEVLINK_PARAM_GENERIC_ID_RESET_DEV_ON_DRV_PROBE, | |
52 | .name = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_NAME, | |
53 | .type = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_TYPE, | |
54 | }, | |
55 | { | |
56 | .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE, | |
57 | .name = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_NAME, | |
58 | .type = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_TYPE, | |
59 | }, | |
60 | { | |
61 | .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_REMOTE_DEV_RESET, | |
62 | .name = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_NAME, | |
63 | .type = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_TYPE, | |
64 | }, | |
65 | { | |
66 | .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH, | |
67 | .name = DEVLINK_PARAM_GENERIC_ENABLE_ETH_NAME, | |
68 | .type = DEVLINK_PARAM_GENERIC_ENABLE_ETH_TYPE, | |
69 | }, | |
70 | { | |
71 | .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA, | |
72 | .name = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_NAME, | |
73 | .type = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_TYPE, | |
74 | }, | |
75 | { | |
76 | .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET, | |
77 | .name = DEVLINK_PARAM_GENERIC_ENABLE_VNET_NAME, | |
78 | .type = DEVLINK_PARAM_GENERIC_ENABLE_VNET_TYPE, | |
79 | }, | |
80 | { | |
81 | .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_IWARP, | |
82 | .name = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_NAME, | |
83 | .type = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_TYPE, | |
84 | }, | |
85 | { | |
86 | .id = DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE, | |
87 | .name = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_NAME, | |
88 | .type = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_TYPE, | |
89 | }, | |
90 | { | |
91 | .id = DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE, | |
92 | .name = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_NAME, | |
93 | .type = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_TYPE, | |
94 | }, | |
95 | }; | |
96 | ||
97 | static int devlink_param_generic_verify(const struct devlink_param *param) | |
98 | { | |
99 | /* verify it match generic parameter by id and name */ | |
100 | if (param->id > DEVLINK_PARAM_GENERIC_ID_MAX) | |
101 | return -EINVAL; | |
102 | if (strcmp(param->name, devlink_param_generic[param->id].name)) | |
103 | return -ENOENT; | |
104 | ||
105 | WARN_ON(param->type != devlink_param_generic[param->id].type); | |
106 | ||
107 | return 0; | |
108 | } | |
109 | ||
110 | static int devlink_param_driver_verify(const struct devlink_param *param) | |
111 | { | |
112 | int i; | |
113 | ||
114 | if (param->id <= DEVLINK_PARAM_GENERIC_ID_MAX) | |
115 | return -EINVAL; | |
116 | /* verify no such name in generic params */ | |
117 | for (i = 0; i <= DEVLINK_PARAM_GENERIC_ID_MAX; i++) | |
118 | if (!strcmp(param->name, devlink_param_generic[i].name)) | |
119 | return -EEXIST; | |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | static struct devlink_param_item * | |
125 | devlink_param_find_by_name(struct xarray *params, const char *param_name) | |
126 | { | |
127 | struct devlink_param_item *param_item; | |
128 | unsigned long param_id; | |
129 | ||
130 | xa_for_each(params, param_id, param_item) { | |
131 | if (!strcmp(param_item->param->name, param_name)) | |
132 | return param_item; | |
133 | } | |
134 | return NULL; | |
135 | } | |
136 | ||
137 | static struct devlink_param_item * | |
138 | devlink_param_find_by_id(struct xarray *params, u32 param_id) | |
139 | { | |
140 | return xa_load(params, param_id); | |
141 | } | |
142 | ||
143 | static bool | |
144 | devlink_param_cmode_is_supported(const struct devlink_param *param, | |
145 | enum devlink_param_cmode cmode) | |
146 | { | |
147 | return test_bit(cmode, ¶m->supported_cmodes); | |
148 | } | |
149 | ||
150 | static int devlink_param_get(struct devlink *devlink, | |
151 | const struct devlink_param *param, | |
152 | struct devlink_param_gset_ctx *ctx) | |
153 | { | |
154 | if (!param->get) | |
155 | return -EOPNOTSUPP; | |
156 | return param->get(devlink, param->id, ctx); | |
157 | } | |
158 | ||
159 | static int devlink_param_set(struct devlink *devlink, | |
160 | const struct devlink_param *param, | |
161 | struct devlink_param_gset_ctx *ctx) | |
162 | { | |
163 | if (!param->set) | |
164 | return -EOPNOTSUPP; | |
165 | return param->set(devlink, param->id, ctx); | |
166 | } | |
167 | ||
168 | static int | |
169 | devlink_param_type_to_nla_type(enum devlink_param_type param_type) | |
170 | { | |
171 | switch (param_type) { | |
172 | case DEVLINK_PARAM_TYPE_U8: | |
173 | return NLA_U8; | |
174 | case DEVLINK_PARAM_TYPE_U16: | |
175 | return NLA_U16; | |
176 | case DEVLINK_PARAM_TYPE_U32: | |
177 | return NLA_U32; | |
178 | case DEVLINK_PARAM_TYPE_STRING: | |
179 | return NLA_STRING; | |
180 | case DEVLINK_PARAM_TYPE_BOOL: | |
181 | return NLA_FLAG; | |
182 | default: | |
183 | return -EINVAL; | |
184 | } | |
185 | } | |
186 | ||
187 | static int | |
188 | devlink_nl_param_value_fill_one(struct sk_buff *msg, | |
189 | enum devlink_param_type type, | |
190 | enum devlink_param_cmode cmode, | |
191 | union devlink_param_value val) | |
192 | { | |
193 | struct nlattr *param_value_attr; | |
194 | ||
195 | param_value_attr = nla_nest_start_noflag(msg, | |
196 | DEVLINK_ATTR_PARAM_VALUE); | |
197 | if (!param_value_attr) | |
198 | goto nla_put_failure; | |
199 | ||
200 | if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_CMODE, cmode)) | |
201 | goto value_nest_cancel; | |
202 | ||
203 | switch (type) { | |
204 | case DEVLINK_PARAM_TYPE_U8: | |
205 | if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu8)) | |
206 | goto value_nest_cancel; | |
207 | break; | |
208 | case DEVLINK_PARAM_TYPE_U16: | |
209 | if (nla_put_u16(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu16)) | |
210 | goto value_nest_cancel; | |
211 | break; | |
212 | case DEVLINK_PARAM_TYPE_U32: | |
213 | if (nla_put_u32(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu32)) | |
214 | goto value_nest_cancel; | |
215 | break; | |
216 | case DEVLINK_PARAM_TYPE_STRING: | |
217 | if (nla_put_string(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, | |
218 | val.vstr)) | |
219 | goto value_nest_cancel; | |
220 | break; | |
221 | case DEVLINK_PARAM_TYPE_BOOL: | |
222 | if (val.vbool && | |
223 | nla_put_flag(msg, DEVLINK_ATTR_PARAM_VALUE_DATA)) | |
224 | goto value_nest_cancel; | |
225 | break; | |
226 | } | |
227 | ||
228 | nla_nest_end(msg, param_value_attr); | |
229 | return 0; | |
230 | ||
231 | value_nest_cancel: | |
232 | nla_nest_cancel(msg, param_value_attr); | |
233 | nla_put_failure: | |
234 | return -EMSGSIZE; | |
235 | } | |
236 | ||
237 | static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink, | |
238 | unsigned int port_index, | |
239 | struct devlink_param_item *param_item, | |
240 | enum devlink_command cmd, | |
241 | u32 portid, u32 seq, int flags) | |
242 | { | |
243 | union devlink_param_value param_value[DEVLINK_PARAM_CMODE_MAX + 1]; | |
244 | bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {}; | |
245 | const struct devlink_param *param = param_item->param; | |
246 | struct devlink_param_gset_ctx ctx; | |
247 | struct nlattr *param_values_list; | |
248 | struct nlattr *param_attr; | |
249 | int nla_type; | |
250 | void *hdr; | |
251 | int err; | |
252 | int i; | |
253 | ||
254 | /* Get value from driver part to driverinit configuration mode */ | |
255 | for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) { | |
256 | if (!devlink_param_cmode_is_supported(param, i)) | |
257 | continue; | |
258 | if (i == DEVLINK_PARAM_CMODE_DRIVERINIT) { | |
259 | if (param_item->driverinit_value_new_valid) | |
260 | param_value[i] = param_item->driverinit_value_new; | |
261 | else if (param_item->driverinit_value_valid) | |
262 | param_value[i] = param_item->driverinit_value; | |
263 | else | |
264 | return -EOPNOTSUPP; | |
265 | } else { | |
266 | ctx.cmode = i; | |
267 | err = devlink_param_get(devlink, param, &ctx); | |
268 | if (err) | |
269 | return err; | |
270 | param_value[i] = ctx.val; | |
271 | } | |
272 | param_value_set[i] = true; | |
273 | } | |
274 | ||
275 | hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); | |
276 | if (!hdr) | |
277 | return -EMSGSIZE; | |
278 | ||
279 | if (devlink_nl_put_handle(msg, devlink)) | |
280 | goto genlmsg_cancel; | |
281 | ||
282 | if (cmd == DEVLINK_CMD_PORT_PARAM_GET || | |
283 | cmd == DEVLINK_CMD_PORT_PARAM_NEW || | |
284 | cmd == DEVLINK_CMD_PORT_PARAM_DEL) | |
285 | if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, port_index)) | |
286 | goto genlmsg_cancel; | |
287 | ||
288 | param_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_PARAM); | |
289 | if (!param_attr) | |
290 | goto genlmsg_cancel; | |
291 | if (nla_put_string(msg, DEVLINK_ATTR_PARAM_NAME, param->name)) | |
292 | goto param_nest_cancel; | |
293 | if (param->generic && nla_put_flag(msg, DEVLINK_ATTR_PARAM_GENERIC)) | |
294 | goto param_nest_cancel; | |
295 | ||
296 | nla_type = devlink_param_type_to_nla_type(param->type); | |
297 | if (nla_type < 0) | |
298 | goto param_nest_cancel; | |
299 | if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_TYPE, nla_type)) | |
300 | goto param_nest_cancel; | |
301 | ||
302 | param_values_list = nla_nest_start_noflag(msg, | |
303 | DEVLINK_ATTR_PARAM_VALUES_LIST); | |
304 | if (!param_values_list) | |
305 | goto param_nest_cancel; | |
306 | ||
307 | for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) { | |
308 | if (!param_value_set[i]) | |
309 | continue; | |
310 | err = devlink_nl_param_value_fill_one(msg, param->type, | |
311 | i, param_value[i]); | |
312 | if (err) | |
313 | goto values_list_nest_cancel; | |
314 | } | |
315 | ||
316 | nla_nest_end(msg, param_values_list); | |
317 | nla_nest_end(msg, param_attr); | |
318 | genlmsg_end(msg, hdr); | |
319 | return 0; | |
320 | ||
321 | values_list_nest_cancel: | |
322 | nla_nest_end(msg, param_values_list); | |
323 | param_nest_cancel: | |
324 | nla_nest_cancel(msg, param_attr); | |
325 | genlmsg_cancel: | |
326 | genlmsg_cancel(msg, hdr); | |
327 | return -EMSGSIZE; | |
328 | } | |
329 | ||
330 | static void devlink_param_notify(struct devlink *devlink, | |
331 | unsigned int port_index, | |
332 | struct devlink_param_item *param_item, | |
333 | enum devlink_command cmd) | |
334 | { | |
335 | struct sk_buff *msg; | |
336 | int err; | |
337 | ||
338 | WARN_ON(cmd != DEVLINK_CMD_PARAM_NEW && cmd != DEVLINK_CMD_PARAM_DEL && | |
339 | cmd != DEVLINK_CMD_PORT_PARAM_NEW && | |
340 | cmd != DEVLINK_CMD_PORT_PARAM_DEL); | |
341 | ||
342 | /* devlink_notify_register() / devlink_notify_unregister() | |
343 | * will replay the notifications if the params are added/removed | |
344 | * outside of the lifetime of the instance. | |
345 | */ | |
cddbff47 | 346 | if (!devl_is_registered(devlink) || !devlink_nl_notify_need(devlink)) |
830c41e1 JP |
347 | return; |
348 | ||
349 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
350 | if (!msg) | |
351 | return; | |
352 | err = devlink_nl_param_fill(msg, devlink, port_index, param_item, cmd, | |
353 | 0, 0, 0); | |
354 | if (err) { | |
355 | nlmsg_free(msg); | |
356 | return; | |
357 | } | |
358 | ||
5648de0b | 359 | devlink_nl_notify_send(devlink, msg); |
830c41e1 JP |
360 | } |
361 | ||
362 | static void devlink_params_notify(struct devlink *devlink, | |
363 | enum devlink_command cmd) | |
364 | { | |
365 | struct devlink_param_item *param_item; | |
366 | unsigned long param_id; | |
367 | ||
368 | xa_for_each(&devlink->params, param_id, param_item) | |
369 | devlink_param_notify(devlink, 0, param_item, cmd); | |
370 | } | |
371 | ||
372 | void devlink_params_notify_register(struct devlink *devlink) | |
373 | { | |
374 | devlink_params_notify(devlink, DEVLINK_CMD_PARAM_NEW); | |
375 | } | |
376 | ||
377 | void devlink_params_notify_unregister(struct devlink *devlink) | |
378 | { | |
379 | devlink_params_notify(devlink, DEVLINK_CMD_PARAM_DEL); | |
380 | } | |
381 | ||
382 | static int devlink_nl_param_get_dump_one(struct sk_buff *msg, | |
383 | struct devlink *devlink, | |
384 | struct netlink_callback *cb, | |
385 | int flags) | |
386 | { | |
387 | struct devlink_nl_dump_state *state = devlink_dump_state(cb); | |
388 | struct devlink_param_item *param_item; | |
389 | unsigned long param_id; | |
390 | int err = 0; | |
391 | ||
392 | xa_for_each_start(&devlink->params, param_id, param_item, state->idx) { | |
393 | err = devlink_nl_param_fill(msg, devlink, 0, param_item, | |
394 | DEVLINK_CMD_PARAM_GET, | |
395 | NETLINK_CB(cb->skb).portid, | |
396 | cb->nlh->nlmsg_seq, flags); | |
397 | if (err == -EOPNOTSUPP) { | |
398 | err = 0; | |
399 | } else if (err) { | |
400 | state->idx = param_id; | |
401 | break; | |
402 | } | |
403 | } | |
404 | ||
405 | return err; | |
406 | } | |
407 | ||
408 | int devlink_nl_param_get_dumpit(struct sk_buff *skb, | |
409 | struct netlink_callback *cb) | |
410 | { | |
411 | return devlink_nl_dumpit(skb, cb, devlink_nl_param_get_dump_one); | |
412 | } | |
413 | ||
414 | static int | |
415 | devlink_param_type_get_from_info(struct genl_info *info, | |
416 | enum devlink_param_type *param_type) | |
417 | { | |
418 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_TYPE)) | |
419 | return -EINVAL; | |
420 | ||
421 | switch (nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_TYPE])) { | |
422 | case NLA_U8: | |
423 | *param_type = DEVLINK_PARAM_TYPE_U8; | |
424 | break; | |
425 | case NLA_U16: | |
426 | *param_type = DEVLINK_PARAM_TYPE_U16; | |
427 | break; | |
428 | case NLA_U32: | |
429 | *param_type = DEVLINK_PARAM_TYPE_U32; | |
430 | break; | |
431 | case NLA_STRING: | |
432 | *param_type = DEVLINK_PARAM_TYPE_STRING; | |
433 | break; | |
434 | case NLA_FLAG: | |
435 | *param_type = DEVLINK_PARAM_TYPE_BOOL; | |
436 | break; | |
437 | default: | |
438 | return -EINVAL; | |
439 | } | |
440 | ||
441 | return 0; | |
442 | } | |
443 | ||
444 | static int | |
445 | devlink_param_value_get_from_info(const struct devlink_param *param, | |
446 | struct genl_info *info, | |
447 | union devlink_param_value *value) | |
448 | { | |
449 | struct nlattr *param_data; | |
450 | int len; | |
451 | ||
452 | param_data = info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA]; | |
453 | ||
454 | if (param->type != DEVLINK_PARAM_TYPE_BOOL && !param_data) | |
455 | return -EINVAL; | |
456 | ||
457 | switch (param->type) { | |
458 | case DEVLINK_PARAM_TYPE_U8: | |
459 | if (nla_len(param_data) != sizeof(u8)) | |
460 | return -EINVAL; | |
461 | value->vu8 = nla_get_u8(param_data); | |
462 | break; | |
463 | case DEVLINK_PARAM_TYPE_U16: | |
464 | if (nla_len(param_data) != sizeof(u16)) | |
465 | return -EINVAL; | |
466 | value->vu16 = nla_get_u16(param_data); | |
467 | break; | |
468 | case DEVLINK_PARAM_TYPE_U32: | |
469 | if (nla_len(param_data) != sizeof(u32)) | |
470 | return -EINVAL; | |
471 | value->vu32 = nla_get_u32(param_data); | |
472 | break; | |
473 | case DEVLINK_PARAM_TYPE_STRING: | |
474 | len = strnlen(nla_data(param_data), nla_len(param_data)); | |
475 | if (len == nla_len(param_data) || | |
476 | len >= __DEVLINK_PARAM_MAX_STRING_VALUE) | |
477 | return -EINVAL; | |
478 | strcpy(value->vstr, nla_data(param_data)); | |
479 | break; | |
480 | case DEVLINK_PARAM_TYPE_BOOL: | |
481 | if (param_data && nla_len(param_data)) | |
482 | return -EINVAL; | |
483 | value->vbool = nla_get_flag(param_data); | |
484 | break; | |
485 | } | |
486 | return 0; | |
487 | } | |
488 | ||
489 | static struct devlink_param_item * | |
490 | devlink_param_get_from_info(struct xarray *params, struct genl_info *info) | |
491 | { | |
492 | char *param_name; | |
493 | ||
494 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_NAME)) | |
495 | return NULL; | |
496 | ||
497 | param_name = nla_data(info->attrs[DEVLINK_ATTR_PARAM_NAME]); | |
498 | return devlink_param_find_by_name(params, param_name); | |
499 | } | |
500 | ||
501 | int devlink_nl_param_get_doit(struct sk_buff *skb, | |
502 | struct genl_info *info) | |
503 | { | |
504 | struct devlink *devlink = info->user_ptr[0]; | |
505 | struct devlink_param_item *param_item; | |
506 | struct sk_buff *msg; | |
507 | int err; | |
508 | ||
509 | param_item = devlink_param_get_from_info(&devlink->params, info); | |
510 | if (!param_item) | |
511 | return -EINVAL; | |
512 | ||
513 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
514 | if (!msg) | |
515 | return -ENOMEM; | |
516 | ||
517 | err = devlink_nl_param_fill(msg, devlink, 0, param_item, | |
518 | DEVLINK_CMD_PARAM_GET, | |
519 | info->snd_portid, info->snd_seq, 0); | |
520 | if (err) { | |
521 | nlmsg_free(msg); | |
522 | return err; | |
523 | } | |
524 | ||
525 | return genlmsg_reply(msg, info); | |
526 | } | |
527 | ||
528 | static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink, | |
529 | unsigned int port_index, | |
530 | struct xarray *params, | |
531 | struct genl_info *info, | |
532 | enum devlink_command cmd) | |
533 | { | |
534 | enum devlink_param_type param_type; | |
535 | struct devlink_param_gset_ctx ctx; | |
536 | enum devlink_param_cmode cmode; | |
537 | struct devlink_param_item *param_item; | |
538 | const struct devlink_param *param; | |
539 | union devlink_param_value value; | |
540 | int err = 0; | |
541 | ||
542 | param_item = devlink_param_get_from_info(params, info); | |
543 | if (!param_item) | |
544 | return -EINVAL; | |
545 | param = param_item->param; | |
546 | err = devlink_param_type_get_from_info(info, ¶m_type); | |
547 | if (err) | |
548 | return err; | |
549 | if (param_type != param->type) | |
550 | return -EINVAL; | |
551 | err = devlink_param_value_get_from_info(param, info, &value); | |
552 | if (err) | |
553 | return err; | |
554 | if (param->validate) { | |
555 | err = param->validate(devlink, param->id, value, info->extack); | |
556 | if (err) | |
557 | return err; | |
558 | } | |
559 | ||
560 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_VALUE_CMODE)) | |
561 | return -EINVAL; | |
562 | cmode = nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_VALUE_CMODE]); | |
563 | if (!devlink_param_cmode_is_supported(param, cmode)) | |
564 | return -EOPNOTSUPP; | |
565 | ||
566 | if (cmode == DEVLINK_PARAM_CMODE_DRIVERINIT) { | |
567 | param_item->driverinit_value_new = value; | |
568 | param_item->driverinit_value_new_valid = true; | |
569 | } else { | |
570 | if (!param->set) | |
571 | return -EOPNOTSUPP; | |
572 | ctx.val = value; | |
573 | ctx.cmode = cmode; | |
574 | err = devlink_param_set(devlink, param, &ctx); | |
575 | if (err) | |
576 | return err; | |
577 | } | |
578 | ||
579 | devlink_param_notify(devlink, port_index, param_item, cmd); | |
580 | return 0; | |
581 | } | |
582 | ||
53590934 | 583 | int devlink_nl_param_set_doit(struct sk_buff *skb, struct genl_info *info) |
830c41e1 JP |
584 | { |
585 | struct devlink *devlink = info->user_ptr[0]; | |
586 | ||
587 | return __devlink_nl_cmd_param_set_doit(devlink, 0, &devlink->params, | |
588 | info, DEVLINK_CMD_PARAM_NEW); | |
589 | } | |
590 | ||
53590934 JP |
591 | int devlink_nl_port_param_get_dumpit(struct sk_buff *msg, |
592 | struct netlink_callback *cb) | |
830c41e1 JP |
593 | { |
594 | NL_SET_ERR_MSG(cb->extack, "Port params are not supported"); | |
595 | return msg->len; | |
596 | } | |
597 | ||
53590934 JP |
598 | int devlink_nl_port_param_get_doit(struct sk_buff *skb, |
599 | struct genl_info *info) | |
830c41e1 JP |
600 | { |
601 | NL_SET_ERR_MSG(info->extack, "Port params are not supported"); | |
602 | return -EINVAL; | |
603 | } | |
604 | ||
53590934 JP |
605 | int devlink_nl_port_param_set_doit(struct sk_buff *skb, |
606 | struct genl_info *info) | |
830c41e1 JP |
607 | { |
608 | NL_SET_ERR_MSG(info->extack, "Port params are not supported"); | |
609 | return -EINVAL; | |
610 | } | |
611 | ||
612 | static int devlink_param_verify(const struct devlink_param *param) | |
613 | { | |
614 | if (!param || !param->name || !param->supported_cmodes) | |
615 | return -EINVAL; | |
616 | if (param->generic) | |
617 | return devlink_param_generic_verify(param); | |
618 | else | |
619 | return devlink_param_driver_verify(param); | |
620 | } | |
621 | ||
622 | static int devlink_param_register(struct devlink *devlink, | |
623 | const struct devlink_param *param) | |
624 | { | |
625 | struct devlink_param_item *param_item; | |
626 | int err; | |
627 | ||
628 | WARN_ON(devlink_param_verify(param)); | |
629 | WARN_ON(devlink_param_find_by_name(&devlink->params, param->name)); | |
630 | ||
631 | if (param->supported_cmodes == BIT(DEVLINK_PARAM_CMODE_DRIVERINIT)) | |
632 | WARN_ON(param->get || param->set); | |
633 | else | |
634 | WARN_ON(!param->get || !param->set); | |
635 | ||
636 | param_item = kzalloc(sizeof(*param_item), GFP_KERNEL); | |
637 | if (!param_item) | |
638 | return -ENOMEM; | |
639 | ||
640 | param_item->param = param; | |
641 | ||
642 | err = xa_insert(&devlink->params, param->id, param_item, GFP_KERNEL); | |
643 | if (err) | |
644 | goto err_xa_insert; | |
645 | ||
646 | devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW); | |
647 | return 0; | |
648 | ||
649 | err_xa_insert: | |
650 | kfree(param_item); | |
651 | return err; | |
652 | } | |
653 | ||
654 | static void devlink_param_unregister(struct devlink *devlink, | |
655 | const struct devlink_param *param) | |
656 | { | |
657 | struct devlink_param_item *param_item; | |
658 | ||
659 | param_item = devlink_param_find_by_id(&devlink->params, param->id); | |
660 | if (WARN_ON(!param_item)) | |
661 | return; | |
662 | devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_DEL); | |
663 | xa_erase(&devlink->params, param->id); | |
664 | kfree(param_item); | |
665 | } | |
666 | ||
667 | /** | |
668 | * devl_params_register - register configuration parameters | |
669 | * | |
670 | * @devlink: devlink | |
671 | * @params: configuration parameters array | |
672 | * @params_count: number of parameters provided | |
673 | * | |
674 | * Register the configuration parameters supported by the driver. | |
675 | */ | |
676 | int devl_params_register(struct devlink *devlink, | |
677 | const struct devlink_param *params, | |
678 | size_t params_count) | |
679 | { | |
680 | const struct devlink_param *param = params; | |
681 | int i, err; | |
682 | ||
683 | lockdep_assert_held(&devlink->lock); | |
684 | ||
685 | for (i = 0; i < params_count; i++, param++) { | |
686 | err = devlink_param_register(devlink, param); | |
687 | if (err) | |
688 | goto rollback; | |
689 | } | |
690 | return 0; | |
691 | ||
692 | rollback: | |
693 | if (!i) | |
694 | return err; | |
695 | ||
696 | for (param--; i > 0; i--, param--) | |
697 | devlink_param_unregister(devlink, param); | |
698 | return err; | |
699 | } | |
700 | EXPORT_SYMBOL_GPL(devl_params_register); | |
701 | ||
702 | int devlink_params_register(struct devlink *devlink, | |
703 | const struct devlink_param *params, | |
704 | size_t params_count) | |
705 | { | |
706 | int err; | |
707 | ||
708 | devl_lock(devlink); | |
709 | err = devl_params_register(devlink, params, params_count); | |
710 | devl_unlock(devlink); | |
711 | return err; | |
712 | } | |
713 | EXPORT_SYMBOL_GPL(devlink_params_register); | |
714 | ||
715 | /** | |
716 | * devl_params_unregister - unregister configuration parameters | |
717 | * @devlink: devlink | |
718 | * @params: configuration parameters to unregister | |
719 | * @params_count: number of parameters provided | |
720 | */ | |
721 | void devl_params_unregister(struct devlink *devlink, | |
722 | const struct devlink_param *params, | |
723 | size_t params_count) | |
724 | { | |
725 | const struct devlink_param *param = params; | |
726 | int i; | |
727 | ||
728 | lockdep_assert_held(&devlink->lock); | |
729 | ||
730 | for (i = 0; i < params_count; i++, param++) | |
731 | devlink_param_unregister(devlink, param); | |
732 | } | |
733 | EXPORT_SYMBOL_GPL(devl_params_unregister); | |
734 | ||
735 | void devlink_params_unregister(struct devlink *devlink, | |
736 | const struct devlink_param *params, | |
737 | size_t params_count) | |
738 | { | |
739 | devl_lock(devlink); | |
740 | devl_params_unregister(devlink, params, params_count); | |
741 | devl_unlock(devlink); | |
742 | } | |
743 | EXPORT_SYMBOL_GPL(devlink_params_unregister); | |
744 | ||
745 | /** | |
746 | * devl_param_driverinit_value_get - get configuration parameter | |
747 | * value for driver initializing | |
748 | * | |
749 | * @devlink: devlink | |
750 | * @param_id: parameter ID | |
751 | * @val: pointer to store the value of parameter in driverinit | |
752 | * configuration mode | |
753 | * | |
754 | * This function should be used by the driver to get driverinit | |
755 | * configuration for initialization after reload command. | |
756 | * | |
757 | * Note that lockless call of this function relies on the | |
758 | * driver to maintain following basic sane behavior: | |
759 | * 1) Driver ensures a call to this function cannot race with | |
760 | * registering/unregistering the parameter with the same parameter ID. | |
761 | * 2) Driver ensures a call to this function cannot race with | |
762 | * devl_param_driverinit_value_set() call with the same parameter ID. | |
763 | * 3) Driver ensures a call to this function cannot race with | |
764 | * reload operation. | |
765 | * If the driver is not able to comply, it has to take the devlink->lock | |
766 | * while calling this. | |
767 | */ | |
768 | int devl_param_driverinit_value_get(struct devlink *devlink, u32 param_id, | |
769 | union devlink_param_value *val) | |
770 | { | |
771 | struct devlink_param_item *param_item; | |
772 | ||
773 | if (WARN_ON(!devlink_reload_supported(devlink->ops))) | |
774 | return -EOPNOTSUPP; | |
775 | ||
776 | param_item = devlink_param_find_by_id(&devlink->params, param_id); | |
777 | if (!param_item) | |
778 | return -EINVAL; | |
779 | ||
780 | if (!param_item->driverinit_value_valid) | |
781 | return -EOPNOTSUPP; | |
782 | ||
783 | if (WARN_ON(!devlink_param_cmode_is_supported(param_item->param, | |
784 | DEVLINK_PARAM_CMODE_DRIVERINIT))) | |
785 | return -EOPNOTSUPP; | |
786 | ||
787 | *val = param_item->driverinit_value; | |
788 | ||
789 | return 0; | |
790 | } | |
791 | EXPORT_SYMBOL_GPL(devl_param_driverinit_value_get); | |
792 | ||
793 | /** | |
794 | * devl_param_driverinit_value_set - set value of configuration | |
795 | * parameter for driverinit | |
796 | * configuration mode | |
797 | * | |
798 | * @devlink: devlink | |
799 | * @param_id: parameter ID | |
800 | * @init_val: value of parameter to set for driverinit configuration mode | |
801 | * | |
802 | * This function should be used by the driver to set driverinit | |
803 | * configuration mode default value. | |
804 | */ | |
805 | void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id, | |
806 | union devlink_param_value init_val) | |
807 | { | |
808 | struct devlink_param_item *param_item; | |
809 | ||
810 | devl_assert_locked(devlink); | |
811 | ||
812 | param_item = devlink_param_find_by_id(&devlink->params, param_id); | |
813 | if (WARN_ON(!param_item)) | |
814 | return; | |
815 | ||
816 | if (WARN_ON(!devlink_param_cmode_is_supported(param_item->param, | |
817 | DEVLINK_PARAM_CMODE_DRIVERINIT))) | |
818 | return; | |
819 | ||
820 | param_item->driverinit_value = init_val; | |
821 | param_item->driverinit_value_valid = true; | |
822 | ||
823 | devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW); | |
824 | } | |
825 | EXPORT_SYMBOL_GPL(devl_param_driverinit_value_set); | |
826 | ||
827 | void devlink_params_driverinit_load_new(struct devlink *devlink) | |
828 | { | |
829 | struct devlink_param_item *param_item; | |
830 | unsigned long param_id; | |
831 | ||
832 | xa_for_each(&devlink->params, param_id, param_item) { | |
833 | if (!devlink_param_cmode_is_supported(param_item->param, | |
834 | DEVLINK_PARAM_CMODE_DRIVERINIT) || | |
835 | !param_item->driverinit_value_new_valid) | |
836 | continue; | |
837 | param_item->driverinit_value = param_item->driverinit_value_new; | |
838 | param_item->driverinit_value_valid = true; | |
839 | param_item->driverinit_value_new_valid = false; | |
840 | } | |
841 | } | |
842 | ||
843 | /** | |
844 | * devl_param_value_changed - notify devlink on a parameter's value | |
845 | * change. Should be called by the driver | |
846 | * right after the change. | |
847 | * | |
848 | * @devlink: devlink | |
849 | * @param_id: parameter ID | |
850 | * | |
851 | * This function should be used by the driver to notify devlink on value | |
852 | * change, excluding driverinit configuration mode. | |
853 | * For driverinit configuration mode driver should use the function | |
854 | */ | |
855 | void devl_param_value_changed(struct devlink *devlink, u32 param_id) | |
856 | { | |
857 | struct devlink_param_item *param_item; | |
858 | ||
859 | param_item = devlink_param_find_by_id(&devlink->params, param_id); | |
860 | WARN_ON(!param_item); | |
861 | ||
862 | devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW); | |
863 | } | |
864 | EXPORT_SYMBOL_GPL(devl_param_value_changed); |