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