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