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 inline bool |
10 | devlink_rate_is_leaf(struct devlink_rate *devlink_rate) |
11 | { |
12 | return devlink_rate->type == DEVLINK_RATE_TYPE_LEAF; |
13 | } |
14 | |
15 | static inline bool |
16 | devlink_rate_is_node(struct devlink_rate *devlink_rate) |
17 | { |
18 | return devlink_rate->type == DEVLINK_RATE_TYPE_NODE; |
19 | } |
20 | |
21 | static struct devlink_rate * |
22 | devlink_rate_leaf_get_from_info(struct devlink *devlink, struct genl_info *info) |
23 | { |
24 | struct devlink_rate *devlink_rate; |
25 | struct devlink_port *devlink_port; |
26 | |
27 | devlink_port = devlink_port_get_from_attrs(devlink, attrs: info->attrs); |
28 | if (IS_ERR(ptr: devlink_port)) |
29 | return ERR_CAST(ptr: devlink_port); |
30 | devlink_rate = devlink_port->devlink_rate; |
31 | return devlink_rate ?: ERR_PTR(error: -ENODEV); |
32 | } |
33 | |
34 | static struct devlink_rate * |
35 | devlink_rate_node_get_by_name(struct devlink *devlink, const char *node_name) |
36 | { |
37 | static struct devlink_rate *devlink_rate; |
38 | |
39 | list_for_each_entry(devlink_rate, &devlink->rate_list, list) { |
40 | if (devlink_rate_is_node(devlink_rate) && |
41 | !strcmp(node_name, devlink_rate->name)) |
42 | return devlink_rate; |
43 | } |
44 | return ERR_PTR(error: -ENODEV); |
45 | } |
46 | |
47 | static struct devlink_rate * |
48 | devlink_rate_node_get_from_attrs(struct devlink *devlink, struct nlattr **attrs) |
49 | { |
50 | const char *rate_node_name; |
51 | size_t len; |
52 | |
53 | if (!attrs[DEVLINK_ATTR_RATE_NODE_NAME]) |
54 | return ERR_PTR(error: -EINVAL); |
55 | rate_node_name = nla_data(nla: attrs[DEVLINK_ATTR_RATE_NODE_NAME]); |
56 | len = strlen(rate_node_name); |
57 | /* Name cannot be empty or decimal number */ |
58 | if (!len || strspn(rate_node_name, "0123456789" ) == len) |
59 | return ERR_PTR(error: -EINVAL); |
60 | |
61 | return devlink_rate_node_get_by_name(devlink, node_name: rate_node_name); |
62 | } |
63 | |
64 | static struct devlink_rate * |
65 | devlink_rate_node_get_from_info(struct devlink *devlink, struct genl_info *info) |
66 | { |
67 | return devlink_rate_node_get_from_attrs(devlink, attrs: info->attrs); |
68 | } |
69 | |
70 | static struct devlink_rate * |
71 | devlink_rate_get_from_info(struct devlink *devlink, struct genl_info *info) |
72 | { |
73 | struct nlattr **attrs = info->attrs; |
74 | |
75 | if (attrs[DEVLINK_ATTR_PORT_INDEX]) |
76 | return devlink_rate_leaf_get_from_info(devlink, info); |
77 | else if (attrs[DEVLINK_ATTR_RATE_NODE_NAME]) |
78 | return devlink_rate_node_get_from_info(devlink, info); |
79 | else |
80 | return ERR_PTR(error: -EINVAL); |
81 | } |
82 | |
83 | static int devlink_nl_rate_fill(struct sk_buff *msg, |
84 | struct devlink_rate *devlink_rate, |
85 | enum devlink_command cmd, u32 portid, u32 seq, |
86 | int flags, struct netlink_ext_ack *extack) |
87 | { |
88 | struct devlink *devlink = devlink_rate->devlink; |
89 | void *hdr; |
90 | |
91 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
92 | if (!hdr) |
93 | return -EMSGSIZE; |
94 | |
95 | if (devlink_nl_put_handle(msg, devlink)) |
96 | goto nla_put_failure; |
97 | |
98 | if (nla_put_u16(skb: msg, attrtype: DEVLINK_ATTR_RATE_TYPE, value: devlink_rate->type)) |
99 | goto nla_put_failure; |
100 | |
101 | if (devlink_rate_is_leaf(devlink_rate)) { |
102 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_PORT_INDEX, |
103 | value: devlink_rate->devlink_port->index)) |
104 | goto nla_put_failure; |
105 | } else if (devlink_rate_is_node(devlink_rate)) { |
106 | if (nla_put_string(skb: msg, attrtype: DEVLINK_ATTR_RATE_NODE_NAME, |
107 | str: devlink_rate->name)) |
108 | goto nla_put_failure; |
109 | } |
110 | |
111 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_RATE_TX_SHARE, |
112 | value: devlink_rate->tx_share, padattr: DEVLINK_ATTR_PAD)) |
113 | goto nla_put_failure; |
114 | |
115 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_RATE_TX_MAX, |
116 | value: devlink_rate->tx_max, padattr: DEVLINK_ATTR_PAD)) |
117 | goto nla_put_failure; |
118 | |
119 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_RATE_TX_PRIORITY, |
120 | value: devlink_rate->tx_priority)) |
121 | goto nla_put_failure; |
122 | |
123 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_RATE_TX_WEIGHT, |
124 | value: devlink_rate->tx_weight)) |
125 | goto nla_put_failure; |
126 | |
127 | if (devlink_rate->parent) |
128 | if (nla_put_string(skb: msg, attrtype: DEVLINK_ATTR_RATE_PARENT_NODE_NAME, |
129 | str: devlink_rate->parent->name)) |
130 | goto nla_put_failure; |
131 | |
132 | genlmsg_end(skb: msg, hdr); |
133 | return 0; |
134 | |
135 | nla_put_failure: |
136 | genlmsg_cancel(skb: msg, hdr); |
137 | return -EMSGSIZE; |
138 | } |
139 | |
140 | static void devlink_rate_notify(struct devlink_rate *devlink_rate, |
141 | enum devlink_command cmd) |
142 | { |
143 | struct devlink *devlink = devlink_rate->devlink; |
144 | struct sk_buff *msg; |
145 | int err; |
146 | |
147 | WARN_ON(cmd != DEVLINK_CMD_RATE_NEW && cmd != DEVLINK_CMD_RATE_DEL); |
148 | |
149 | if (!xa_get_mark(&devlinks, index: devlink->index, DEVLINK_REGISTERED)) |
150 | return; |
151 | |
152 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
153 | if (!msg) |
154 | return; |
155 | |
156 | err = devlink_nl_rate_fill(msg, devlink_rate, cmd, portid: 0, seq: 0, flags: 0, NULL); |
157 | if (err) { |
158 | nlmsg_free(skb: msg); |
159 | return; |
160 | } |
161 | |
162 | genlmsg_multicast_netns(family: &devlink_nl_family, net: devlink_net(devlink), skb: msg, |
163 | portid: 0, group: DEVLINK_MCGRP_CONFIG, GFP_KERNEL); |
164 | } |
165 | |
166 | void devlink_rates_notify_register(struct devlink *devlink) |
167 | { |
168 | struct devlink_rate *rate_node; |
169 | |
170 | list_for_each_entry(rate_node, &devlink->rate_list, list) |
171 | devlink_rate_notify(devlink_rate: rate_node, cmd: DEVLINK_CMD_RATE_NEW); |
172 | } |
173 | |
174 | void devlink_rates_notify_unregister(struct devlink *devlink) |
175 | { |
176 | struct devlink_rate *rate_node; |
177 | |
178 | list_for_each_entry_reverse(rate_node, &devlink->rate_list, list) |
179 | devlink_rate_notify(devlink_rate: rate_node, cmd: DEVLINK_CMD_RATE_DEL); |
180 | } |
181 | |
182 | static int |
183 | devlink_nl_rate_get_dump_one(struct sk_buff *msg, struct devlink *devlink, |
184 | struct netlink_callback *cb, int flags) |
185 | { |
186 | struct devlink_nl_dump_state *state = devlink_dump_state(cb); |
187 | struct devlink_rate *devlink_rate; |
188 | int idx = 0; |
189 | int err = 0; |
190 | |
191 | list_for_each_entry(devlink_rate, &devlink->rate_list, list) { |
192 | enum devlink_command cmd = DEVLINK_CMD_RATE_NEW; |
193 | u32 id = NETLINK_CB(cb->skb).portid; |
194 | |
195 | if (idx < state->idx) { |
196 | idx++; |
197 | continue; |
198 | } |
199 | err = devlink_nl_rate_fill(msg, devlink_rate, cmd, portid: id, |
200 | seq: cb->nlh->nlmsg_seq, flags, NULL); |
201 | if (err) { |
202 | state->idx = idx; |
203 | break; |
204 | } |
205 | idx++; |
206 | } |
207 | |
208 | return err; |
209 | } |
210 | |
211 | int devlink_nl_rate_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) |
212 | { |
213 | return devlink_nl_dumpit(msg: skb, cb, dump_one: devlink_nl_rate_get_dump_one); |
214 | } |
215 | |
216 | int devlink_nl_rate_get_doit(struct sk_buff *skb, struct genl_info *info) |
217 | { |
218 | struct devlink *devlink = info->user_ptr[0]; |
219 | struct devlink_rate *devlink_rate; |
220 | struct sk_buff *msg; |
221 | int err; |
222 | |
223 | devlink_rate = devlink_rate_get_from_info(devlink, info); |
224 | if (IS_ERR(ptr: devlink_rate)) |
225 | return PTR_ERR(ptr: devlink_rate); |
226 | |
227 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
228 | if (!msg) |
229 | return -ENOMEM; |
230 | |
231 | err = devlink_nl_rate_fill(msg, devlink_rate, cmd: DEVLINK_CMD_RATE_NEW, |
232 | portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
233 | extack: info->extack); |
234 | if (err) { |
235 | nlmsg_free(skb: msg); |
236 | return err; |
237 | } |
238 | |
239 | return genlmsg_reply(skb: msg, info); |
240 | } |
241 | |
242 | static bool |
243 | devlink_rate_is_parent_node(struct devlink_rate *devlink_rate, |
244 | struct devlink_rate *parent) |
245 | { |
246 | while (parent) { |
247 | if (parent == devlink_rate) |
248 | return true; |
249 | parent = parent->parent; |
250 | } |
251 | return false; |
252 | } |
253 | |
254 | static int |
255 | devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate, |
256 | struct genl_info *info, |
257 | struct nlattr *nla_parent) |
258 | { |
259 | struct devlink *devlink = devlink_rate->devlink; |
260 | const char *parent_name = nla_data(nla: nla_parent); |
261 | const struct devlink_ops *ops = devlink->ops; |
262 | size_t len = strlen(parent_name); |
263 | struct devlink_rate *parent; |
264 | int err = -EOPNOTSUPP; |
265 | |
266 | parent = devlink_rate->parent; |
267 | |
268 | if (parent && !len) { |
269 | if (devlink_rate_is_leaf(devlink_rate)) |
270 | err = ops->rate_leaf_parent_set(devlink_rate, NULL, |
271 | devlink_rate->priv, NULL, |
272 | info->extack); |
273 | else if (devlink_rate_is_node(devlink_rate)) |
274 | err = ops->rate_node_parent_set(devlink_rate, NULL, |
275 | devlink_rate->priv, NULL, |
276 | info->extack); |
277 | if (err) |
278 | return err; |
279 | |
280 | refcount_dec(r: &parent->refcnt); |
281 | devlink_rate->parent = NULL; |
282 | } else if (len) { |
283 | parent = devlink_rate_node_get_by_name(devlink, node_name: parent_name); |
284 | if (IS_ERR(ptr: parent)) |
285 | return -ENODEV; |
286 | |
287 | if (parent == devlink_rate) { |
288 | NL_SET_ERR_MSG(info->extack, "Parent to self is not allowed" ); |
289 | return -EINVAL; |
290 | } |
291 | |
292 | if (devlink_rate_is_node(devlink_rate) && |
293 | devlink_rate_is_parent_node(devlink_rate, parent: parent->parent)) { |
294 | NL_SET_ERR_MSG(info->extack, "Node is already a parent of parent node." ); |
295 | return -EEXIST; |
296 | } |
297 | |
298 | if (devlink_rate_is_leaf(devlink_rate)) |
299 | err = ops->rate_leaf_parent_set(devlink_rate, parent, |
300 | devlink_rate->priv, parent->priv, |
301 | info->extack); |
302 | else if (devlink_rate_is_node(devlink_rate)) |
303 | err = ops->rate_node_parent_set(devlink_rate, parent, |
304 | devlink_rate->priv, parent->priv, |
305 | info->extack); |
306 | if (err) |
307 | return err; |
308 | |
309 | if (devlink_rate->parent) |
310 | /* we're reassigning to other parent in this case */ |
311 | refcount_dec(r: &devlink_rate->parent->refcnt); |
312 | |
313 | refcount_inc(r: &parent->refcnt); |
314 | devlink_rate->parent = parent; |
315 | } |
316 | |
317 | return 0; |
318 | } |
319 | |
320 | static int devlink_nl_rate_set(struct devlink_rate *devlink_rate, |
321 | const struct devlink_ops *ops, |
322 | struct genl_info *info) |
323 | { |
324 | struct nlattr *nla_parent, **attrs = info->attrs; |
325 | int err = -EOPNOTSUPP; |
326 | u32 priority; |
327 | u32 weight; |
328 | u64 rate; |
329 | |
330 | if (attrs[DEVLINK_ATTR_RATE_TX_SHARE]) { |
331 | rate = nla_get_u64(nla: attrs[DEVLINK_ATTR_RATE_TX_SHARE]); |
332 | if (devlink_rate_is_leaf(devlink_rate)) |
333 | err = ops->rate_leaf_tx_share_set(devlink_rate, devlink_rate->priv, |
334 | rate, info->extack); |
335 | else if (devlink_rate_is_node(devlink_rate)) |
336 | err = ops->rate_node_tx_share_set(devlink_rate, devlink_rate->priv, |
337 | rate, info->extack); |
338 | if (err) |
339 | return err; |
340 | devlink_rate->tx_share = rate; |
341 | } |
342 | |
343 | if (attrs[DEVLINK_ATTR_RATE_TX_MAX]) { |
344 | rate = nla_get_u64(nla: attrs[DEVLINK_ATTR_RATE_TX_MAX]); |
345 | if (devlink_rate_is_leaf(devlink_rate)) |
346 | err = ops->rate_leaf_tx_max_set(devlink_rate, devlink_rate->priv, |
347 | rate, info->extack); |
348 | else if (devlink_rate_is_node(devlink_rate)) |
349 | err = ops->rate_node_tx_max_set(devlink_rate, devlink_rate->priv, |
350 | rate, info->extack); |
351 | if (err) |
352 | return err; |
353 | devlink_rate->tx_max = rate; |
354 | } |
355 | |
356 | if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]) { |
357 | priority = nla_get_u32(nla: attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]); |
358 | if (devlink_rate_is_leaf(devlink_rate)) |
359 | err = ops->rate_leaf_tx_priority_set(devlink_rate, devlink_rate->priv, |
360 | priority, info->extack); |
361 | else if (devlink_rate_is_node(devlink_rate)) |
362 | err = ops->rate_node_tx_priority_set(devlink_rate, devlink_rate->priv, |
363 | priority, info->extack); |
364 | |
365 | if (err) |
366 | return err; |
367 | devlink_rate->tx_priority = priority; |
368 | } |
369 | |
370 | if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]) { |
371 | weight = nla_get_u32(nla: attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]); |
372 | if (devlink_rate_is_leaf(devlink_rate)) |
373 | err = ops->rate_leaf_tx_weight_set(devlink_rate, devlink_rate->priv, |
374 | weight, info->extack); |
375 | else if (devlink_rate_is_node(devlink_rate)) |
376 | err = ops->rate_node_tx_weight_set(devlink_rate, devlink_rate->priv, |
377 | weight, info->extack); |
378 | |
379 | if (err) |
380 | return err; |
381 | devlink_rate->tx_weight = weight; |
382 | } |
383 | |
384 | nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]; |
385 | if (nla_parent) { |
386 | err = devlink_nl_rate_parent_node_set(devlink_rate, info, |
387 | nla_parent); |
388 | if (err) |
389 | return err; |
390 | } |
391 | |
392 | return 0; |
393 | } |
394 | |
395 | static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops, |
396 | struct genl_info *info, |
397 | enum devlink_rate_type type) |
398 | { |
399 | struct nlattr **attrs = info->attrs; |
400 | |
401 | if (type == DEVLINK_RATE_TYPE_LEAF) { |
402 | if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_leaf_tx_share_set) { |
403 | NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the leafs" ); |
404 | return false; |
405 | } |
406 | if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_leaf_tx_max_set) { |
407 | NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the leafs" ); |
408 | return false; |
409 | } |
410 | if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] && |
411 | !ops->rate_leaf_parent_set) { |
412 | NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the leafs" ); |
413 | return false; |
414 | } |
415 | if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_leaf_tx_priority_set) { |
416 | NL_SET_ERR_MSG_ATTR(info->extack, |
417 | attrs[DEVLINK_ATTR_RATE_TX_PRIORITY], |
418 | "TX priority set isn't supported for the leafs" ); |
419 | return false; |
420 | } |
421 | if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_leaf_tx_weight_set) { |
422 | NL_SET_ERR_MSG_ATTR(info->extack, |
423 | attrs[DEVLINK_ATTR_RATE_TX_WEIGHT], |
424 | "TX weight set isn't supported for the leafs" ); |
425 | return false; |
426 | } |
427 | } else if (type == DEVLINK_RATE_TYPE_NODE) { |
428 | if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_node_tx_share_set) { |
429 | NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the nodes" ); |
430 | return false; |
431 | } |
432 | if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_node_tx_max_set) { |
433 | NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the nodes" ); |
434 | return false; |
435 | } |
436 | if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] && |
437 | !ops->rate_node_parent_set) { |
438 | NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the nodes" ); |
439 | return false; |
440 | } |
441 | if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_node_tx_priority_set) { |
442 | NL_SET_ERR_MSG_ATTR(info->extack, |
443 | attrs[DEVLINK_ATTR_RATE_TX_PRIORITY], |
444 | "TX priority set isn't supported for the nodes" ); |
445 | return false; |
446 | } |
447 | if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_node_tx_weight_set) { |
448 | NL_SET_ERR_MSG_ATTR(info->extack, |
449 | attrs[DEVLINK_ATTR_RATE_TX_WEIGHT], |
450 | "TX weight set isn't supported for the nodes" ); |
451 | return false; |
452 | } |
453 | } else { |
454 | WARN(1, "Unknown type of rate object" ); |
455 | return false; |
456 | } |
457 | |
458 | return true; |
459 | } |
460 | |
461 | int devlink_nl_rate_set_doit(struct sk_buff *skb, struct genl_info *info) |
462 | { |
463 | struct devlink *devlink = info->user_ptr[0]; |
464 | struct devlink_rate *devlink_rate; |
465 | const struct devlink_ops *ops; |
466 | int err; |
467 | |
468 | devlink_rate = devlink_rate_get_from_info(devlink, info); |
469 | if (IS_ERR(ptr: devlink_rate)) |
470 | return PTR_ERR(ptr: devlink_rate); |
471 | |
472 | ops = devlink->ops; |
473 | if (!ops || !devlink_rate_set_ops_supported(ops, info, type: devlink_rate->type)) |
474 | return -EOPNOTSUPP; |
475 | |
476 | err = devlink_nl_rate_set(devlink_rate, ops, info); |
477 | |
478 | if (!err) |
479 | devlink_rate_notify(devlink_rate, cmd: DEVLINK_CMD_RATE_NEW); |
480 | return err; |
481 | } |
482 | |
483 | int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info) |
484 | { |
485 | struct devlink *devlink = info->user_ptr[0]; |
486 | struct devlink_rate *rate_node; |
487 | const struct devlink_ops *ops; |
488 | int err; |
489 | |
490 | ops = devlink->ops; |
491 | if (!ops || !ops->rate_node_new || !ops->rate_node_del) { |
492 | NL_SET_ERR_MSG(info->extack, "Rate nodes aren't supported" ); |
493 | return -EOPNOTSUPP; |
494 | } |
495 | |
496 | if (!devlink_rate_set_ops_supported(ops, info, type: DEVLINK_RATE_TYPE_NODE)) |
497 | return -EOPNOTSUPP; |
498 | |
499 | rate_node = devlink_rate_node_get_from_attrs(devlink, attrs: info->attrs); |
500 | if (!IS_ERR(ptr: rate_node)) |
501 | return -EEXIST; |
502 | else if (rate_node == ERR_PTR(error: -EINVAL)) |
503 | return -EINVAL; |
504 | |
505 | rate_node = kzalloc(size: sizeof(*rate_node), GFP_KERNEL); |
506 | if (!rate_node) |
507 | return -ENOMEM; |
508 | |
509 | rate_node->devlink = devlink; |
510 | rate_node->type = DEVLINK_RATE_TYPE_NODE; |
511 | rate_node->name = nla_strdup(nla: info->attrs[DEVLINK_ATTR_RATE_NODE_NAME], GFP_KERNEL); |
512 | if (!rate_node->name) { |
513 | err = -ENOMEM; |
514 | goto err_strdup; |
515 | } |
516 | |
517 | err = ops->rate_node_new(rate_node, &rate_node->priv, info->extack); |
518 | if (err) |
519 | goto err_node_new; |
520 | |
521 | err = devlink_nl_rate_set(devlink_rate: rate_node, ops, info); |
522 | if (err) |
523 | goto err_rate_set; |
524 | |
525 | refcount_set(r: &rate_node->refcnt, n: 1); |
526 | list_add(new: &rate_node->list, head: &devlink->rate_list); |
527 | devlink_rate_notify(devlink_rate: rate_node, cmd: DEVLINK_CMD_RATE_NEW); |
528 | return 0; |
529 | |
530 | err_rate_set: |
531 | ops->rate_node_del(rate_node, rate_node->priv, info->extack); |
532 | err_node_new: |
533 | kfree(objp: rate_node->name); |
534 | err_strdup: |
535 | kfree(objp: rate_node); |
536 | return err; |
537 | } |
538 | |
539 | int devlink_nl_rate_del_doit(struct sk_buff *skb, struct genl_info *info) |
540 | { |
541 | struct devlink *devlink = info->user_ptr[0]; |
542 | struct devlink_rate *rate_node; |
543 | int err; |
544 | |
545 | rate_node = devlink_rate_node_get_from_info(devlink, info); |
546 | if (IS_ERR(ptr: rate_node)) |
547 | return PTR_ERR(ptr: rate_node); |
548 | |
549 | if (refcount_read(r: &rate_node->refcnt) > 1) { |
550 | NL_SET_ERR_MSG(info->extack, "Node has children. Cannot delete node." ); |
551 | return -EBUSY; |
552 | } |
553 | |
554 | devlink_rate_notify(devlink_rate: rate_node, cmd: DEVLINK_CMD_RATE_DEL); |
555 | err = devlink->ops->rate_node_del(rate_node, rate_node->priv, |
556 | info->extack); |
557 | if (rate_node->parent) |
558 | refcount_dec(r: &rate_node->parent->refcnt); |
559 | list_del(entry: &rate_node->list); |
560 | kfree(objp: rate_node->name); |
561 | kfree(objp: rate_node); |
562 | return err; |
563 | } |
564 | |
565 | int devlink_rate_nodes_check(struct devlink *devlink, u16 mode, |
566 | struct netlink_ext_ack *extack) |
567 | { |
568 | struct devlink_rate *devlink_rate; |
569 | |
570 | list_for_each_entry(devlink_rate, &devlink->rate_list, list) |
571 | if (devlink_rate_is_node(devlink_rate)) { |
572 | NL_SET_ERR_MSG(extack, "Rate node(s) exists." ); |
573 | return -EBUSY; |
574 | } |
575 | return 0; |
576 | } |
577 | |
578 | /** |
579 | * devl_rate_node_create - create devlink rate node |
580 | * @devlink: devlink instance |
581 | * @priv: driver private data |
582 | * @node_name: name of the resulting node |
583 | * @parent: parent devlink_rate struct |
584 | * |
585 | * Create devlink rate object of type node |
586 | */ |
587 | struct devlink_rate * |
588 | devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, |
589 | struct devlink_rate *parent) |
590 | { |
591 | struct devlink_rate *rate_node; |
592 | |
593 | rate_node = devlink_rate_node_get_by_name(devlink, node_name); |
594 | if (!IS_ERR(ptr: rate_node)) |
595 | return ERR_PTR(error: -EEXIST); |
596 | |
597 | rate_node = kzalloc(size: sizeof(*rate_node), GFP_KERNEL); |
598 | if (!rate_node) |
599 | return ERR_PTR(error: -ENOMEM); |
600 | |
601 | if (parent) { |
602 | rate_node->parent = parent; |
603 | refcount_inc(r: &rate_node->parent->refcnt); |
604 | } |
605 | |
606 | rate_node->type = DEVLINK_RATE_TYPE_NODE; |
607 | rate_node->devlink = devlink; |
608 | rate_node->priv = priv; |
609 | |
610 | rate_node->name = kstrdup(s: node_name, GFP_KERNEL); |
611 | if (!rate_node->name) { |
612 | kfree(objp: rate_node); |
613 | return ERR_PTR(error: -ENOMEM); |
614 | } |
615 | |
616 | refcount_set(r: &rate_node->refcnt, n: 1); |
617 | list_add(new: &rate_node->list, head: &devlink->rate_list); |
618 | devlink_rate_notify(devlink_rate: rate_node, cmd: DEVLINK_CMD_RATE_NEW); |
619 | return rate_node; |
620 | } |
621 | EXPORT_SYMBOL_GPL(devl_rate_node_create); |
622 | |
623 | /** |
624 | * devl_rate_leaf_create - create devlink rate leaf |
625 | * @devlink_port: devlink port object to create rate object on |
626 | * @priv: driver private data |
627 | * @parent: parent devlink_rate struct |
628 | * |
629 | * Create devlink rate object of type leaf on provided @devlink_port. |
630 | */ |
631 | int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv, |
632 | struct devlink_rate *parent) |
633 | { |
634 | struct devlink *devlink = devlink_port->devlink; |
635 | struct devlink_rate *devlink_rate; |
636 | |
637 | devl_assert_locked(devlink: devlink_port->devlink); |
638 | |
639 | if (WARN_ON(devlink_port->devlink_rate)) |
640 | return -EBUSY; |
641 | |
642 | devlink_rate = kzalloc(size: sizeof(*devlink_rate), GFP_KERNEL); |
643 | if (!devlink_rate) |
644 | return -ENOMEM; |
645 | |
646 | if (parent) { |
647 | devlink_rate->parent = parent; |
648 | refcount_inc(r: &devlink_rate->parent->refcnt); |
649 | } |
650 | |
651 | devlink_rate->type = DEVLINK_RATE_TYPE_LEAF; |
652 | devlink_rate->devlink = devlink; |
653 | devlink_rate->devlink_port = devlink_port; |
654 | devlink_rate->priv = priv; |
655 | list_add_tail(new: &devlink_rate->list, head: &devlink->rate_list); |
656 | devlink_port->devlink_rate = devlink_rate; |
657 | devlink_rate_notify(devlink_rate, cmd: DEVLINK_CMD_RATE_NEW); |
658 | |
659 | return 0; |
660 | } |
661 | EXPORT_SYMBOL_GPL(devl_rate_leaf_create); |
662 | |
663 | /** |
664 | * devl_rate_leaf_destroy - destroy devlink rate leaf |
665 | * |
666 | * @devlink_port: devlink port linked to the rate object |
667 | * |
668 | * Destroy the devlink rate object of type leaf on provided @devlink_port. |
669 | */ |
670 | void devl_rate_leaf_destroy(struct devlink_port *devlink_port) |
671 | { |
672 | struct devlink_rate *devlink_rate = devlink_port->devlink_rate; |
673 | |
674 | devl_assert_locked(devlink: devlink_port->devlink); |
675 | if (!devlink_rate) |
676 | return; |
677 | |
678 | devlink_rate_notify(devlink_rate, cmd: DEVLINK_CMD_RATE_DEL); |
679 | if (devlink_rate->parent) |
680 | refcount_dec(r: &devlink_rate->parent->refcnt); |
681 | list_del(entry: &devlink_rate->list); |
682 | devlink_port->devlink_rate = NULL; |
683 | kfree(objp: devlink_rate); |
684 | } |
685 | EXPORT_SYMBOL_GPL(devl_rate_leaf_destroy); |
686 | |
687 | /** |
688 | * devl_rate_nodes_destroy - destroy all devlink rate nodes on device |
689 | * @devlink: devlink instance |
690 | * |
691 | * Unset parent for all rate objects and destroy all rate nodes |
692 | * on specified device. |
693 | */ |
694 | void devl_rate_nodes_destroy(struct devlink *devlink) |
695 | { |
696 | static struct devlink_rate *devlink_rate, *tmp; |
697 | const struct devlink_ops *ops = devlink->ops; |
698 | |
699 | devl_assert_locked(devlink); |
700 | |
701 | list_for_each_entry(devlink_rate, &devlink->rate_list, list) { |
702 | if (!devlink_rate->parent) |
703 | continue; |
704 | |
705 | refcount_dec(r: &devlink_rate->parent->refcnt); |
706 | if (devlink_rate_is_leaf(devlink_rate)) |
707 | ops->rate_leaf_parent_set(devlink_rate, NULL, devlink_rate->priv, |
708 | NULL, NULL); |
709 | else if (devlink_rate_is_node(devlink_rate)) |
710 | ops->rate_node_parent_set(devlink_rate, NULL, devlink_rate->priv, |
711 | NULL, NULL); |
712 | } |
713 | list_for_each_entry_safe(devlink_rate, tmp, &devlink->rate_list, list) { |
714 | if (devlink_rate_is_node(devlink_rate)) { |
715 | ops->rate_node_del(devlink_rate, devlink_rate->priv, NULL); |
716 | list_del(entry: &devlink_rate->list); |
717 | kfree(objp: devlink_rate->name); |
718 | kfree(objp: devlink_rate); |
719 | } |
720 | } |
721 | } |
722 | EXPORT_SYMBOL_GPL(devl_rate_nodes_destroy); |
723 | |