162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 462306a36Sopenharmony_ci * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "devl_internal.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_cistatic inline bool 1062306a36Sopenharmony_cidevlink_rate_is_leaf(struct devlink_rate *devlink_rate) 1162306a36Sopenharmony_ci{ 1262306a36Sopenharmony_ci return devlink_rate->type == DEVLINK_RATE_TYPE_LEAF; 1362306a36Sopenharmony_ci} 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic inline bool 1662306a36Sopenharmony_cidevlink_rate_is_node(struct devlink_rate *devlink_rate) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci return devlink_rate->type == DEVLINK_RATE_TYPE_NODE; 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct devlink_rate * 2262306a36Sopenharmony_cidevlink_rate_leaf_get_from_info(struct devlink *devlink, struct genl_info *info) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct devlink_rate *devlink_rate; 2562306a36Sopenharmony_ci struct devlink_port *devlink_port; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci devlink_port = devlink_port_get_from_attrs(devlink, info->attrs); 2862306a36Sopenharmony_ci if (IS_ERR(devlink_port)) 2962306a36Sopenharmony_ci return ERR_CAST(devlink_port); 3062306a36Sopenharmony_ci devlink_rate = devlink_port->devlink_rate; 3162306a36Sopenharmony_ci return devlink_rate ?: ERR_PTR(-ENODEV); 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic struct devlink_rate * 3562306a36Sopenharmony_cidevlink_rate_node_get_by_name(struct devlink *devlink, const char *node_name) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci static struct devlink_rate *devlink_rate; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci list_for_each_entry(devlink_rate, &devlink->rate_list, list) { 4062306a36Sopenharmony_ci if (devlink_rate_is_node(devlink_rate) && 4162306a36Sopenharmony_ci !strcmp(node_name, devlink_rate->name)) 4262306a36Sopenharmony_ci return devlink_rate; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct devlink_rate * 4862306a36Sopenharmony_cidevlink_rate_node_get_from_attrs(struct devlink *devlink, struct nlattr **attrs) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci const char *rate_node_name; 5162306a36Sopenharmony_ci size_t len; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (!attrs[DEVLINK_ATTR_RATE_NODE_NAME]) 5462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 5562306a36Sopenharmony_ci rate_node_name = nla_data(attrs[DEVLINK_ATTR_RATE_NODE_NAME]); 5662306a36Sopenharmony_ci len = strlen(rate_node_name); 5762306a36Sopenharmony_ci /* Name cannot be empty or decimal number */ 5862306a36Sopenharmony_ci if (!len || strspn(rate_node_name, "0123456789") == len) 5962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return devlink_rate_node_get_by_name(devlink, rate_node_name); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct devlink_rate * 6562306a36Sopenharmony_cidevlink_rate_node_get_from_info(struct devlink *devlink, struct genl_info *info) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci return devlink_rate_node_get_from_attrs(devlink, info->attrs); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct devlink_rate * 7162306a36Sopenharmony_cidevlink_rate_get_from_info(struct devlink *devlink, struct genl_info *info) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct nlattr **attrs = info->attrs; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_PORT_INDEX]) 7662306a36Sopenharmony_ci return devlink_rate_leaf_get_from_info(devlink, info); 7762306a36Sopenharmony_ci else if (attrs[DEVLINK_ATTR_RATE_NODE_NAME]) 7862306a36Sopenharmony_ci return devlink_rate_node_get_from_info(devlink, info); 7962306a36Sopenharmony_ci else 8062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int devlink_nl_rate_fill(struct sk_buff *msg, 8462306a36Sopenharmony_ci struct devlink_rate *devlink_rate, 8562306a36Sopenharmony_ci enum devlink_command cmd, u32 portid, u32 seq, 8662306a36Sopenharmony_ci int flags, struct netlink_ext_ack *extack) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct devlink *devlink = devlink_rate->devlink; 8962306a36Sopenharmony_ci void *hdr; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); 9262306a36Sopenharmony_ci if (!hdr) 9362306a36Sopenharmony_ci return -EMSGSIZE; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (devlink_nl_put_handle(msg, devlink)) 9662306a36Sopenharmony_ci goto nla_put_failure; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (nla_put_u16(msg, DEVLINK_ATTR_RATE_TYPE, devlink_rate->type)) 9962306a36Sopenharmony_ci goto nla_put_failure; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (devlink_rate_is_leaf(devlink_rate)) { 10262306a36Sopenharmony_ci if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, 10362306a36Sopenharmony_ci devlink_rate->devlink_port->index)) 10462306a36Sopenharmony_ci goto nla_put_failure; 10562306a36Sopenharmony_ci } else if (devlink_rate_is_node(devlink_rate)) { 10662306a36Sopenharmony_ci if (nla_put_string(msg, DEVLINK_ATTR_RATE_NODE_NAME, 10762306a36Sopenharmony_ci devlink_rate->name)) 10862306a36Sopenharmony_ci goto nla_put_failure; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_SHARE, 11262306a36Sopenharmony_ci devlink_rate->tx_share, DEVLINK_ATTR_PAD)) 11362306a36Sopenharmony_ci goto nla_put_failure; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_MAX, 11662306a36Sopenharmony_ci devlink_rate->tx_max, DEVLINK_ATTR_PAD)) 11762306a36Sopenharmony_ci goto nla_put_failure; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_PRIORITY, 12062306a36Sopenharmony_ci devlink_rate->tx_priority)) 12162306a36Sopenharmony_ci goto nla_put_failure; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_WEIGHT, 12462306a36Sopenharmony_ci devlink_rate->tx_weight)) 12562306a36Sopenharmony_ci goto nla_put_failure; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (devlink_rate->parent) 12862306a36Sopenharmony_ci if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME, 12962306a36Sopenharmony_ci devlink_rate->parent->name)) 13062306a36Sopenharmony_ci goto nla_put_failure; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci genlmsg_end(msg, hdr); 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cinla_put_failure: 13662306a36Sopenharmony_ci genlmsg_cancel(msg, hdr); 13762306a36Sopenharmony_ci return -EMSGSIZE; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void devlink_rate_notify(struct devlink_rate *devlink_rate, 14162306a36Sopenharmony_ci enum devlink_command cmd) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct devlink *devlink = devlink_rate->devlink; 14462306a36Sopenharmony_ci struct sk_buff *msg; 14562306a36Sopenharmony_ci int err; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci WARN_ON(cmd != DEVLINK_CMD_RATE_NEW && cmd != DEVLINK_CMD_RATE_DEL); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED)) 15062306a36Sopenharmony_ci return; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 15362306a36Sopenharmony_ci if (!msg) 15462306a36Sopenharmony_ci return; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci err = devlink_nl_rate_fill(msg, devlink_rate, cmd, 0, 0, 0, NULL); 15762306a36Sopenharmony_ci if (err) { 15862306a36Sopenharmony_ci nlmsg_free(msg); 15962306a36Sopenharmony_ci return; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), msg, 16362306a36Sopenharmony_ci 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_civoid devlink_rates_notify_register(struct devlink *devlink) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct devlink_rate *rate_node; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci list_for_each_entry(rate_node, &devlink->rate_list, list) 17162306a36Sopenharmony_ci devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_civoid devlink_rates_notify_unregister(struct devlink *devlink) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct devlink_rate *rate_node; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci list_for_each_entry_reverse(rate_node, &devlink->rate_list, list) 17962306a36Sopenharmony_ci devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int 18362306a36Sopenharmony_cidevlink_nl_rate_get_dump_one(struct sk_buff *msg, struct devlink *devlink, 18462306a36Sopenharmony_ci struct netlink_callback *cb, int flags) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct devlink_nl_dump_state *state = devlink_dump_state(cb); 18762306a36Sopenharmony_ci struct devlink_rate *devlink_rate; 18862306a36Sopenharmony_ci int idx = 0; 18962306a36Sopenharmony_ci int err = 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci list_for_each_entry(devlink_rate, &devlink->rate_list, list) { 19262306a36Sopenharmony_ci enum devlink_command cmd = DEVLINK_CMD_RATE_NEW; 19362306a36Sopenharmony_ci u32 id = NETLINK_CB(cb->skb).portid; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (idx < state->idx) { 19662306a36Sopenharmony_ci idx++; 19762306a36Sopenharmony_ci continue; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci err = devlink_nl_rate_fill(msg, devlink_rate, cmd, id, 20062306a36Sopenharmony_ci cb->nlh->nlmsg_seq, flags, NULL); 20162306a36Sopenharmony_ci if (err) { 20262306a36Sopenharmony_ci state->idx = idx; 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci idx++; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return err; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ciint devlink_nl_rate_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci return devlink_nl_dumpit(skb, cb, devlink_nl_rate_get_dump_one); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ciint devlink_nl_rate_get_doit(struct sk_buff *skb, struct genl_info *info) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 21962306a36Sopenharmony_ci struct devlink_rate *devlink_rate; 22062306a36Sopenharmony_ci struct sk_buff *msg; 22162306a36Sopenharmony_ci int err; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci devlink_rate = devlink_rate_get_from_info(devlink, info); 22462306a36Sopenharmony_ci if (IS_ERR(devlink_rate)) 22562306a36Sopenharmony_ci return PTR_ERR(devlink_rate); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 22862306a36Sopenharmony_ci if (!msg) 22962306a36Sopenharmony_ci return -ENOMEM; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci err = devlink_nl_rate_fill(msg, devlink_rate, DEVLINK_CMD_RATE_NEW, 23262306a36Sopenharmony_ci info->snd_portid, info->snd_seq, 0, 23362306a36Sopenharmony_ci info->extack); 23462306a36Sopenharmony_ci if (err) { 23562306a36Sopenharmony_ci nlmsg_free(msg); 23662306a36Sopenharmony_ci return err; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return genlmsg_reply(msg, info); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic bool 24362306a36Sopenharmony_cidevlink_rate_is_parent_node(struct devlink_rate *devlink_rate, 24462306a36Sopenharmony_ci struct devlink_rate *parent) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci while (parent) { 24762306a36Sopenharmony_ci if (parent == devlink_rate) 24862306a36Sopenharmony_ci return true; 24962306a36Sopenharmony_ci parent = parent->parent; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci return false; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int 25562306a36Sopenharmony_cidevlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate, 25662306a36Sopenharmony_ci struct genl_info *info, 25762306a36Sopenharmony_ci struct nlattr *nla_parent) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct devlink *devlink = devlink_rate->devlink; 26062306a36Sopenharmony_ci const char *parent_name = nla_data(nla_parent); 26162306a36Sopenharmony_ci const struct devlink_ops *ops = devlink->ops; 26262306a36Sopenharmony_ci size_t len = strlen(parent_name); 26362306a36Sopenharmony_ci struct devlink_rate *parent; 26462306a36Sopenharmony_ci int err = -EOPNOTSUPP; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci parent = devlink_rate->parent; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (parent && !len) { 26962306a36Sopenharmony_ci if (devlink_rate_is_leaf(devlink_rate)) 27062306a36Sopenharmony_ci err = ops->rate_leaf_parent_set(devlink_rate, NULL, 27162306a36Sopenharmony_ci devlink_rate->priv, NULL, 27262306a36Sopenharmony_ci info->extack); 27362306a36Sopenharmony_ci else if (devlink_rate_is_node(devlink_rate)) 27462306a36Sopenharmony_ci err = ops->rate_node_parent_set(devlink_rate, NULL, 27562306a36Sopenharmony_ci devlink_rate->priv, NULL, 27662306a36Sopenharmony_ci info->extack); 27762306a36Sopenharmony_ci if (err) 27862306a36Sopenharmony_ci return err; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci refcount_dec(&parent->refcnt); 28162306a36Sopenharmony_ci devlink_rate->parent = NULL; 28262306a36Sopenharmony_ci } else if (len) { 28362306a36Sopenharmony_ci parent = devlink_rate_node_get_by_name(devlink, parent_name); 28462306a36Sopenharmony_ci if (IS_ERR(parent)) 28562306a36Sopenharmony_ci return -ENODEV; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (parent == devlink_rate) { 28862306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, "Parent to self is not allowed"); 28962306a36Sopenharmony_ci return -EINVAL; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (devlink_rate_is_node(devlink_rate) && 29362306a36Sopenharmony_ci devlink_rate_is_parent_node(devlink_rate, parent->parent)) { 29462306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, "Node is already a parent of parent node."); 29562306a36Sopenharmony_ci return -EEXIST; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (devlink_rate_is_leaf(devlink_rate)) 29962306a36Sopenharmony_ci err = ops->rate_leaf_parent_set(devlink_rate, parent, 30062306a36Sopenharmony_ci devlink_rate->priv, parent->priv, 30162306a36Sopenharmony_ci info->extack); 30262306a36Sopenharmony_ci else if (devlink_rate_is_node(devlink_rate)) 30362306a36Sopenharmony_ci err = ops->rate_node_parent_set(devlink_rate, parent, 30462306a36Sopenharmony_ci devlink_rate->priv, parent->priv, 30562306a36Sopenharmony_ci info->extack); 30662306a36Sopenharmony_ci if (err) 30762306a36Sopenharmony_ci return err; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (devlink_rate->parent) 31062306a36Sopenharmony_ci /* we're reassigning to other parent in this case */ 31162306a36Sopenharmony_ci refcount_dec(&devlink_rate->parent->refcnt); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci refcount_inc(&parent->refcnt); 31462306a36Sopenharmony_ci devlink_rate->parent = parent; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int devlink_nl_rate_set(struct devlink_rate *devlink_rate, 32162306a36Sopenharmony_ci const struct devlink_ops *ops, 32262306a36Sopenharmony_ci struct genl_info *info) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct nlattr *nla_parent, **attrs = info->attrs; 32562306a36Sopenharmony_ci int err = -EOPNOTSUPP; 32662306a36Sopenharmony_ci u32 priority; 32762306a36Sopenharmony_ci u32 weight; 32862306a36Sopenharmony_ci u64 rate; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_SHARE]) { 33162306a36Sopenharmony_ci rate = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_SHARE]); 33262306a36Sopenharmony_ci if (devlink_rate_is_leaf(devlink_rate)) 33362306a36Sopenharmony_ci err = ops->rate_leaf_tx_share_set(devlink_rate, devlink_rate->priv, 33462306a36Sopenharmony_ci rate, info->extack); 33562306a36Sopenharmony_ci else if (devlink_rate_is_node(devlink_rate)) 33662306a36Sopenharmony_ci err = ops->rate_node_tx_share_set(devlink_rate, devlink_rate->priv, 33762306a36Sopenharmony_ci rate, info->extack); 33862306a36Sopenharmony_ci if (err) 33962306a36Sopenharmony_ci return err; 34062306a36Sopenharmony_ci devlink_rate->tx_share = rate; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_MAX]) { 34462306a36Sopenharmony_ci rate = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_MAX]); 34562306a36Sopenharmony_ci if (devlink_rate_is_leaf(devlink_rate)) 34662306a36Sopenharmony_ci err = ops->rate_leaf_tx_max_set(devlink_rate, devlink_rate->priv, 34762306a36Sopenharmony_ci rate, info->extack); 34862306a36Sopenharmony_ci else if (devlink_rate_is_node(devlink_rate)) 34962306a36Sopenharmony_ci err = ops->rate_node_tx_max_set(devlink_rate, devlink_rate->priv, 35062306a36Sopenharmony_ci rate, info->extack); 35162306a36Sopenharmony_ci if (err) 35262306a36Sopenharmony_ci return err; 35362306a36Sopenharmony_ci devlink_rate->tx_max = rate; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]) { 35762306a36Sopenharmony_ci priority = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]); 35862306a36Sopenharmony_ci if (devlink_rate_is_leaf(devlink_rate)) 35962306a36Sopenharmony_ci err = ops->rate_leaf_tx_priority_set(devlink_rate, devlink_rate->priv, 36062306a36Sopenharmony_ci priority, info->extack); 36162306a36Sopenharmony_ci else if (devlink_rate_is_node(devlink_rate)) 36262306a36Sopenharmony_ci err = ops->rate_node_tx_priority_set(devlink_rate, devlink_rate->priv, 36362306a36Sopenharmony_ci priority, info->extack); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (err) 36662306a36Sopenharmony_ci return err; 36762306a36Sopenharmony_ci devlink_rate->tx_priority = priority; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]) { 37162306a36Sopenharmony_ci weight = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]); 37262306a36Sopenharmony_ci if (devlink_rate_is_leaf(devlink_rate)) 37362306a36Sopenharmony_ci err = ops->rate_leaf_tx_weight_set(devlink_rate, devlink_rate->priv, 37462306a36Sopenharmony_ci weight, info->extack); 37562306a36Sopenharmony_ci else if (devlink_rate_is_node(devlink_rate)) 37662306a36Sopenharmony_ci err = ops->rate_node_tx_weight_set(devlink_rate, devlink_rate->priv, 37762306a36Sopenharmony_ci weight, info->extack); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (err) 38062306a36Sopenharmony_ci return err; 38162306a36Sopenharmony_ci devlink_rate->tx_weight = weight; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]; 38562306a36Sopenharmony_ci if (nla_parent) { 38662306a36Sopenharmony_ci err = devlink_nl_rate_parent_node_set(devlink_rate, info, 38762306a36Sopenharmony_ci nla_parent); 38862306a36Sopenharmony_ci if (err) 38962306a36Sopenharmony_ci return err; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic bool devlink_rate_set_ops_supported(const struct devlink_ops *ops, 39662306a36Sopenharmony_ci struct genl_info *info, 39762306a36Sopenharmony_ci enum devlink_rate_type type) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct nlattr **attrs = info->attrs; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (type == DEVLINK_RATE_TYPE_LEAF) { 40262306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_leaf_tx_share_set) { 40362306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the leafs"); 40462306a36Sopenharmony_ci return false; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_leaf_tx_max_set) { 40762306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the leafs"); 40862306a36Sopenharmony_ci return false; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] && 41162306a36Sopenharmony_ci !ops->rate_leaf_parent_set) { 41262306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the leafs"); 41362306a36Sopenharmony_ci return false; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_leaf_tx_priority_set) { 41662306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 41762306a36Sopenharmony_ci attrs[DEVLINK_ATTR_RATE_TX_PRIORITY], 41862306a36Sopenharmony_ci "TX priority set isn't supported for the leafs"); 41962306a36Sopenharmony_ci return false; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_leaf_tx_weight_set) { 42262306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 42362306a36Sopenharmony_ci attrs[DEVLINK_ATTR_RATE_TX_WEIGHT], 42462306a36Sopenharmony_ci "TX weight set isn't supported for the leafs"); 42562306a36Sopenharmony_ci return false; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci } else if (type == DEVLINK_RATE_TYPE_NODE) { 42862306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_node_tx_share_set) { 42962306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the nodes"); 43062306a36Sopenharmony_ci return false; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_node_tx_max_set) { 43362306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the nodes"); 43462306a36Sopenharmony_ci return false; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] && 43762306a36Sopenharmony_ci !ops->rate_node_parent_set) { 43862306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the nodes"); 43962306a36Sopenharmony_ci return false; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_node_tx_priority_set) { 44262306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 44362306a36Sopenharmony_ci attrs[DEVLINK_ATTR_RATE_TX_PRIORITY], 44462306a36Sopenharmony_ci "TX priority set isn't supported for the nodes"); 44562306a36Sopenharmony_ci return false; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_node_tx_weight_set) { 44862306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 44962306a36Sopenharmony_ci attrs[DEVLINK_ATTR_RATE_TX_WEIGHT], 45062306a36Sopenharmony_ci "TX weight set isn't supported for the nodes"); 45162306a36Sopenharmony_ci return false; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } else { 45462306a36Sopenharmony_ci WARN(1, "Unknown type of rate object"); 45562306a36Sopenharmony_ci return false; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return true; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ciint devlink_nl_cmd_rate_set_doit(struct sk_buff *skb, struct genl_info *info) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 46462306a36Sopenharmony_ci struct devlink_rate *devlink_rate; 46562306a36Sopenharmony_ci const struct devlink_ops *ops; 46662306a36Sopenharmony_ci int err; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci devlink_rate = devlink_rate_get_from_info(devlink, info); 46962306a36Sopenharmony_ci if (IS_ERR(devlink_rate)) 47062306a36Sopenharmony_ci return PTR_ERR(devlink_rate); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ops = devlink->ops; 47362306a36Sopenharmony_ci if (!ops || !devlink_rate_set_ops_supported(ops, info, devlink_rate->type)) 47462306a36Sopenharmony_ci return -EOPNOTSUPP; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci err = devlink_nl_rate_set(devlink_rate, ops, info); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (!err) 47962306a36Sopenharmony_ci devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW); 48062306a36Sopenharmony_ci return err; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ciint devlink_nl_cmd_rate_new_doit(struct sk_buff *skb, struct genl_info *info) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 48662306a36Sopenharmony_ci struct devlink_rate *rate_node; 48762306a36Sopenharmony_ci const struct devlink_ops *ops; 48862306a36Sopenharmony_ci int err; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci ops = devlink->ops; 49162306a36Sopenharmony_ci if (!ops || !ops->rate_node_new || !ops->rate_node_del) { 49262306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, "Rate nodes aren't supported"); 49362306a36Sopenharmony_ci return -EOPNOTSUPP; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (!devlink_rate_set_ops_supported(ops, info, DEVLINK_RATE_TYPE_NODE)) 49762306a36Sopenharmony_ci return -EOPNOTSUPP; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci rate_node = devlink_rate_node_get_from_attrs(devlink, info->attrs); 50062306a36Sopenharmony_ci if (!IS_ERR(rate_node)) 50162306a36Sopenharmony_ci return -EEXIST; 50262306a36Sopenharmony_ci else if (rate_node == ERR_PTR(-EINVAL)) 50362306a36Sopenharmony_ci return -EINVAL; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci rate_node = kzalloc(sizeof(*rate_node), GFP_KERNEL); 50662306a36Sopenharmony_ci if (!rate_node) 50762306a36Sopenharmony_ci return -ENOMEM; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci rate_node->devlink = devlink; 51062306a36Sopenharmony_ci rate_node->type = DEVLINK_RATE_TYPE_NODE; 51162306a36Sopenharmony_ci rate_node->name = nla_strdup(info->attrs[DEVLINK_ATTR_RATE_NODE_NAME], GFP_KERNEL); 51262306a36Sopenharmony_ci if (!rate_node->name) { 51362306a36Sopenharmony_ci err = -ENOMEM; 51462306a36Sopenharmony_ci goto err_strdup; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci err = ops->rate_node_new(rate_node, &rate_node->priv, info->extack); 51862306a36Sopenharmony_ci if (err) 51962306a36Sopenharmony_ci goto err_node_new; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci err = devlink_nl_rate_set(rate_node, ops, info); 52262306a36Sopenharmony_ci if (err) 52362306a36Sopenharmony_ci goto err_rate_set; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci refcount_set(&rate_node->refcnt, 1); 52662306a36Sopenharmony_ci list_add(&rate_node->list, &devlink->rate_list); 52762306a36Sopenharmony_ci devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW); 52862306a36Sopenharmony_ci return 0; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cierr_rate_set: 53162306a36Sopenharmony_ci ops->rate_node_del(rate_node, rate_node->priv, info->extack); 53262306a36Sopenharmony_cierr_node_new: 53362306a36Sopenharmony_ci kfree(rate_node->name); 53462306a36Sopenharmony_cierr_strdup: 53562306a36Sopenharmony_ci kfree(rate_node); 53662306a36Sopenharmony_ci return err; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ciint devlink_nl_cmd_rate_del_doit(struct sk_buff *skb, struct genl_info *info) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct devlink *devlink = info->user_ptr[0]; 54262306a36Sopenharmony_ci struct devlink_rate *rate_node; 54362306a36Sopenharmony_ci int err; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci rate_node = devlink_rate_node_get_from_info(devlink, info); 54662306a36Sopenharmony_ci if (IS_ERR(rate_node)) 54762306a36Sopenharmony_ci return PTR_ERR(rate_node); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (refcount_read(&rate_node->refcnt) > 1) { 55062306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, "Node has children. Cannot delete node."); 55162306a36Sopenharmony_ci return -EBUSY; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL); 55562306a36Sopenharmony_ci err = devlink->ops->rate_node_del(rate_node, rate_node->priv, 55662306a36Sopenharmony_ci info->extack); 55762306a36Sopenharmony_ci if (rate_node->parent) 55862306a36Sopenharmony_ci refcount_dec(&rate_node->parent->refcnt); 55962306a36Sopenharmony_ci list_del(&rate_node->list); 56062306a36Sopenharmony_ci kfree(rate_node->name); 56162306a36Sopenharmony_ci kfree(rate_node); 56262306a36Sopenharmony_ci return err; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ciint devlink_rate_nodes_check(struct devlink *devlink, u16 mode, 56662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct devlink_rate *devlink_rate; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci list_for_each_entry(devlink_rate, &devlink->rate_list, list) 57162306a36Sopenharmony_ci if (devlink_rate_is_node(devlink_rate)) { 57262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Rate node(s) exists."); 57362306a36Sopenharmony_ci return -EBUSY; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci/** 57962306a36Sopenharmony_ci * devl_rate_node_create - create devlink rate node 58062306a36Sopenharmony_ci * @devlink: devlink instance 58162306a36Sopenharmony_ci * @priv: driver private data 58262306a36Sopenharmony_ci * @node_name: name of the resulting node 58362306a36Sopenharmony_ci * @parent: parent devlink_rate struct 58462306a36Sopenharmony_ci * 58562306a36Sopenharmony_ci * Create devlink rate object of type node 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_cistruct devlink_rate * 58862306a36Sopenharmony_cidevl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, 58962306a36Sopenharmony_ci struct devlink_rate *parent) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct devlink_rate *rate_node; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci rate_node = devlink_rate_node_get_by_name(devlink, node_name); 59462306a36Sopenharmony_ci if (!IS_ERR(rate_node)) 59562306a36Sopenharmony_ci return ERR_PTR(-EEXIST); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci rate_node = kzalloc(sizeof(*rate_node), GFP_KERNEL); 59862306a36Sopenharmony_ci if (!rate_node) 59962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (parent) { 60262306a36Sopenharmony_ci rate_node->parent = parent; 60362306a36Sopenharmony_ci refcount_inc(&rate_node->parent->refcnt); 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci rate_node->type = DEVLINK_RATE_TYPE_NODE; 60762306a36Sopenharmony_ci rate_node->devlink = devlink; 60862306a36Sopenharmony_ci rate_node->priv = priv; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci rate_node->name = kstrdup(node_name, GFP_KERNEL); 61162306a36Sopenharmony_ci if (!rate_node->name) { 61262306a36Sopenharmony_ci kfree(rate_node); 61362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci refcount_set(&rate_node->refcnt, 1); 61762306a36Sopenharmony_ci list_add(&rate_node->list, &devlink->rate_list); 61862306a36Sopenharmony_ci devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW); 61962306a36Sopenharmony_ci return rate_node; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devl_rate_node_create); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/** 62462306a36Sopenharmony_ci * devl_rate_leaf_create - create devlink rate leaf 62562306a36Sopenharmony_ci * @devlink_port: devlink port object to create rate object on 62662306a36Sopenharmony_ci * @priv: driver private data 62762306a36Sopenharmony_ci * @parent: parent devlink_rate struct 62862306a36Sopenharmony_ci * 62962306a36Sopenharmony_ci * Create devlink rate object of type leaf on provided @devlink_port. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ciint devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv, 63262306a36Sopenharmony_ci struct devlink_rate *parent) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct devlink *devlink = devlink_port->devlink; 63562306a36Sopenharmony_ci struct devlink_rate *devlink_rate; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci devl_assert_locked(devlink_port->devlink); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (WARN_ON(devlink_port->devlink_rate)) 64062306a36Sopenharmony_ci return -EBUSY; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci devlink_rate = kzalloc(sizeof(*devlink_rate), GFP_KERNEL); 64362306a36Sopenharmony_ci if (!devlink_rate) 64462306a36Sopenharmony_ci return -ENOMEM; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (parent) { 64762306a36Sopenharmony_ci devlink_rate->parent = parent; 64862306a36Sopenharmony_ci refcount_inc(&devlink_rate->parent->refcnt); 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci devlink_rate->type = DEVLINK_RATE_TYPE_LEAF; 65262306a36Sopenharmony_ci devlink_rate->devlink = devlink; 65362306a36Sopenharmony_ci devlink_rate->devlink_port = devlink_port; 65462306a36Sopenharmony_ci devlink_rate->priv = priv; 65562306a36Sopenharmony_ci list_add_tail(&devlink_rate->list, &devlink->rate_list); 65662306a36Sopenharmony_ci devlink_port->devlink_rate = devlink_rate; 65762306a36Sopenharmony_ci devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci return 0; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devl_rate_leaf_create); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci/** 66462306a36Sopenharmony_ci * devl_rate_leaf_destroy - destroy devlink rate leaf 66562306a36Sopenharmony_ci * 66662306a36Sopenharmony_ci * @devlink_port: devlink port linked to the rate object 66762306a36Sopenharmony_ci * 66862306a36Sopenharmony_ci * Destroy the devlink rate object of type leaf on provided @devlink_port. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_civoid devl_rate_leaf_destroy(struct devlink_port *devlink_port) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct devlink_rate *devlink_rate = devlink_port->devlink_rate; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci devl_assert_locked(devlink_port->devlink); 67562306a36Sopenharmony_ci if (!devlink_rate) 67662306a36Sopenharmony_ci return; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_DEL); 67962306a36Sopenharmony_ci if (devlink_rate->parent) 68062306a36Sopenharmony_ci refcount_dec(&devlink_rate->parent->refcnt); 68162306a36Sopenharmony_ci list_del(&devlink_rate->list); 68262306a36Sopenharmony_ci devlink_port->devlink_rate = NULL; 68362306a36Sopenharmony_ci kfree(devlink_rate); 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devl_rate_leaf_destroy); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci/** 68862306a36Sopenharmony_ci * devl_rate_nodes_destroy - destroy all devlink rate nodes on device 68962306a36Sopenharmony_ci * @devlink: devlink instance 69062306a36Sopenharmony_ci * 69162306a36Sopenharmony_ci * Unset parent for all rate objects and destroy all rate nodes 69262306a36Sopenharmony_ci * on specified device. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_civoid devl_rate_nodes_destroy(struct devlink *devlink) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci static struct devlink_rate *devlink_rate, *tmp; 69762306a36Sopenharmony_ci const struct devlink_ops *ops = devlink->ops; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci devl_assert_locked(devlink); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci list_for_each_entry(devlink_rate, &devlink->rate_list, list) { 70262306a36Sopenharmony_ci if (!devlink_rate->parent) 70362306a36Sopenharmony_ci continue; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci refcount_dec(&devlink_rate->parent->refcnt); 70662306a36Sopenharmony_ci if (devlink_rate_is_leaf(devlink_rate)) 70762306a36Sopenharmony_ci ops->rate_leaf_parent_set(devlink_rate, NULL, devlink_rate->priv, 70862306a36Sopenharmony_ci NULL, NULL); 70962306a36Sopenharmony_ci else if (devlink_rate_is_node(devlink_rate)) 71062306a36Sopenharmony_ci ops->rate_node_parent_set(devlink_rate, NULL, devlink_rate->priv, 71162306a36Sopenharmony_ci NULL, NULL); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci list_for_each_entry_safe(devlink_rate, tmp, &devlink->rate_list, list) { 71462306a36Sopenharmony_ci if (devlink_rate_is_node(devlink_rate)) { 71562306a36Sopenharmony_ci ops->rate_node_del(devlink_rate, devlink_rate->priv, NULL); 71662306a36Sopenharmony_ci list_del(&devlink_rate->list); 71762306a36Sopenharmony_ci kfree(devlink_rate->name); 71862306a36Sopenharmony_ci kfree(devlink_rate); 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devl_rate_nodes_destroy); 723