17cc7194eSJiri Pirko // SPDX-License-Identifier: GPL-2.0-or-later 27cc7194eSJiri Pirko /* 37cc7194eSJiri Pirko * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 47cc7194eSJiri Pirko * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> 57cc7194eSJiri Pirko */ 67cc7194eSJiri Pirko 77cc7194eSJiri Pirko #include "devl_internal.h" 87cc7194eSJiri Pirko 97cc7194eSJiri Pirko static inline bool 107cc7194eSJiri Pirko devlink_rate_is_leaf(struct devlink_rate *devlink_rate) 117cc7194eSJiri Pirko { 127cc7194eSJiri Pirko return devlink_rate->type == DEVLINK_RATE_TYPE_LEAF; 137cc7194eSJiri Pirko } 147cc7194eSJiri Pirko 157cc7194eSJiri Pirko static inline bool 167cc7194eSJiri Pirko devlink_rate_is_node(struct devlink_rate *devlink_rate) 177cc7194eSJiri Pirko { 187cc7194eSJiri Pirko return devlink_rate->type == DEVLINK_RATE_TYPE_NODE; 197cc7194eSJiri Pirko } 207cc7194eSJiri Pirko 217cc7194eSJiri Pirko static struct devlink_rate * 227cc7194eSJiri Pirko devlink_rate_leaf_get_from_info(struct devlink *devlink, struct genl_info *info) 237cc7194eSJiri Pirko { 247cc7194eSJiri Pirko struct devlink_rate *devlink_rate; 257cc7194eSJiri Pirko struct devlink_port *devlink_port; 267cc7194eSJiri Pirko 277cc7194eSJiri Pirko devlink_port = devlink_port_get_from_attrs(devlink, info->attrs); 287cc7194eSJiri Pirko if (IS_ERR(devlink_port)) 297cc7194eSJiri Pirko return ERR_CAST(devlink_port); 307cc7194eSJiri Pirko devlink_rate = devlink_port->devlink_rate; 317cc7194eSJiri Pirko return devlink_rate ?: ERR_PTR(-ENODEV); 327cc7194eSJiri Pirko } 337cc7194eSJiri Pirko 347cc7194eSJiri Pirko static struct devlink_rate * 357cc7194eSJiri Pirko devlink_rate_node_get_by_name(struct devlink *devlink, const char *node_name) 367cc7194eSJiri Pirko { 377cc7194eSJiri Pirko static struct devlink_rate *devlink_rate; 387cc7194eSJiri Pirko 397cc7194eSJiri Pirko list_for_each_entry(devlink_rate, &devlink->rate_list, list) { 407cc7194eSJiri Pirko if (devlink_rate_is_node(devlink_rate) && 417cc7194eSJiri Pirko !strcmp(node_name, devlink_rate->name)) 427cc7194eSJiri Pirko return devlink_rate; 437cc7194eSJiri Pirko } 447cc7194eSJiri Pirko return ERR_PTR(-ENODEV); 457cc7194eSJiri Pirko } 467cc7194eSJiri Pirko 477cc7194eSJiri Pirko static struct devlink_rate * 487cc7194eSJiri Pirko devlink_rate_node_get_from_attrs(struct devlink *devlink, struct nlattr **attrs) 497cc7194eSJiri Pirko { 507cc7194eSJiri Pirko const char *rate_node_name; 517cc7194eSJiri Pirko size_t len; 527cc7194eSJiri Pirko 537cc7194eSJiri Pirko if (!attrs[DEVLINK_ATTR_RATE_NODE_NAME]) 547cc7194eSJiri Pirko return ERR_PTR(-EINVAL); 557cc7194eSJiri Pirko rate_node_name = nla_data(attrs[DEVLINK_ATTR_RATE_NODE_NAME]); 567cc7194eSJiri Pirko len = strlen(rate_node_name); 577cc7194eSJiri Pirko /* Name cannot be empty or decimal number */ 587cc7194eSJiri Pirko if (!len || strspn(rate_node_name, "0123456789") == len) 597cc7194eSJiri Pirko return ERR_PTR(-EINVAL); 607cc7194eSJiri Pirko 617cc7194eSJiri Pirko return devlink_rate_node_get_by_name(devlink, rate_node_name); 627cc7194eSJiri Pirko } 637cc7194eSJiri Pirko 647cc7194eSJiri Pirko static struct devlink_rate * 657cc7194eSJiri Pirko devlink_rate_node_get_from_info(struct devlink *devlink, struct genl_info *info) 667cc7194eSJiri Pirko { 677cc7194eSJiri Pirko return devlink_rate_node_get_from_attrs(devlink, info->attrs); 687cc7194eSJiri Pirko } 697cc7194eSJiri Pirko 707cc7194eSJiri Pirko static struct devlink_rate * 717cc7194eSJiri Pirko devlink_rate_get_from_info(struct devlink *devlink, struct genl_info *info) 727cc7194eSJiri Pirko { 737cc7194eSJiri Pirko struct nlattr **attrs = info->attrs; 747cc7194eSJiri Pirko 757cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_PORT_INDEX]) 767cc7194eSJiri Pirko return devlink_rate_leaf_get_from_info(devlink, info); 777cc7194eSJiri Pirko else if (attrs[DEVLINK_ATTR_RATE_NODE_NAME]) 787cc7194eSJiri Pirko return devlink_rate_node_get_from_info(devlink, info); 797cc7194eSJiri Pirko else 807cc7194eSJiri Pirko return ERR_PTR(-EINVAL); 817cc7194eSJiri Pirko } 827cc7194eSJiri Pirko 837cc7194eSJiri Pirko static int devlink_nl_rate_fill(struct sk_buff *msg, 847cc7194eSJiri Pirko struct devlink_rate *devlink_rate, 857cc7194eSJiri Pirko enum devlink_command cmd, u32 portid, u32 seq, 867cc7194eSJiri Pirko int flags, struct netlink_ext_ack *extack) 877cc7194eSJiri Pirko { 887cc7194eSJiri Pirko struct devlink *devlink = devlink_rate->devlink; 897cc7194eSJiri Pirko void *hdr; 907cc7194eSJiri Pirko 917cc7194eSJiri Pirko hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); 927cc7194eSJiri Pirko if (!hdr) 937cc7194eSJiri Pirko return -EMSGSIZE; 947cc7194eSJiri Pirko 957cc7194eSJiri Pirko if (devlink_nl_put_handle(msg, devlink)) 967cc7194eSJiri Pirko goto nla_put_failure; 977cc7194eSJiri Pirko 987cc7194eSJiri Pirko if (nla_put_u16(msg, DEVLINK_ATTR_RATE_TYPE, devlink_rate->type)) 997cc7194eSJiri Pirko goto nla_put_failure; 1007cc7194eSJiri Pirko 1017cc7194eSJiri Pirko if (devlink_rate_is_leaf(devlink_rate)) { 1027cc7194eSJiri Pirko if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, 1037cc7194eSJiri Pirko devlink_rate->devlink_port->index)) 1047cc7194eSJiri Pirko goto nla_put_failure; 1057cc7194eSJiri Pirko } else if (devlink_rate_is_node(devlink_rate)) { 1067cc7194eSJiri Pirko if (nla_put_string(msg, DEVLINK_ATTR_RATE_NODE_NAME, 1077cc7194eSJiri Pirko devlink_rate->name)) 1087cc7194eSJiri Pirko goto nla_put_failure; 1097cc7194eSJiri Pirko } 1107cc7194eSJiri Pirko 1117cc7194eSJiri Pirko if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_SHARE, 1127cc7194eSJiri Pirko devlink_rate->tx_share, DEVLINK_ATTR_PAD)) 1137cc7194eSJiri Pirko goto nla_put_failure; 1147cc7194eSJiri Pirko 1157cc7194eSJiri Pirko if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_MAX, 1167cc7194eSJiri Pirko devlink_rate->tx_max, DEVLINK_ATTR_PAD)) 1177cc7194eSJiri Pirko goto nla_put_failure; 1187cc7194eSJiri Pirko 1197cc7194eSJiri Pirko if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_PRIORITY, 1207cc7194eSJiri Pirko devlink_rate->tx_priority)) 1217cc7194eSJiri Pirko goto nla_put_failure; 1227cc7194eSJiri Pirko 1237cc7194eSJiri Pirko if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_WEIGHT, 1247cc7194eSJiri Pirko devlink_rate->tx_weight)) 1257cc7194eSJiri Pirko goto nla_put_failure; 1267cc7194eSJiri Pirko 1277cc7194eSJiri Pirko if (devlink_rate->parent) 1287cc7194eSJiri Pirko if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME, 1297cc7194eSJiri Pirko devlink_rate->parent->name)) 1307cc7194eSJiri Pirko goto nla_put_failure; 1317cc7194eSJiri Pirko 1327cc7194eSJiri Pirko genlmsg_end(msg, hdr); 1337cc7194eSJiri Pirko return 0; 1347cc7194eSJiri Pirko 1357cc7194eSJiri Pirko nla_put_failure: 1367cc7194eSJiri Pirko genlmsg_cancel(msg, hdr); 1377cc7194eSJiri Pirko return -EMSGSIZE; 1387cc7194eSJiri Pirko } 1397cc7194eSJiri Pirko 1407cc7194eSJiri Pirko static void devlink_rate_notify(struct devlink_rate *devlink_rate, 1417cc7194eSJiri Pirko enum devlink_command cmd) 1427cc7194eSJiri Pirko { 1437cc7194eSJiri Pirko struct devlink *devlink = devlink_rate->devlink; 1447cc7194eSJiri Pirko struct sk_buff *msg; 1457cc7194eSJiri Pirko int err; 1467cc7194eSJiri Pirko 1477cc7194eSJiri Pirko WARN_ON(cmd != DEVLINK_CMD_RATE_NEW && cmd != DEVLINK_CMD_RATE_DEL); 1487cc7194eSJiri Pirko 149cddbff47SJiri Pirko if (!devl_is_registered(devlink) || !devlink_nl_notify_need(devlink)) 1507cc7194eSJiri Pirko return; 1517cc7194eSJiri Pirko 1527cc7194eSJiri Pirko msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 1537cc7194eSJiri Pirko if (!msg) 1547cc7194eSJiri Pirko return; 1557cc7194eSJiri Pirko 1567cc7194eSJiri Pirko err = devlink_nl_rate_fill(msg, devlink_rate, cmd, 0, 0, 0, NULL); 1577cc7194eSJiri Pirko if (err) { 1587cc7194eSJiri Pirko nlmsg_free(msg); 1597cc7194eSJiri Pirko return; 1607cc7194eSJiri Pirko } 1617cc7194eSJiri Pirko 1627cc7194eSJiri Pirko genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), msg, 1637cc7194eSJiri Pirko 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL); 1647cc7194eSJiri Pirko } 1657cc7194eSJiri Pirko 1667cc7194eSJiri Pirko void devlink_rates_notify_register(struct devlink *devlink) 1677cc7194eSJiri Pirko { 1687cc7194eSJiri Pirko struct devlink_rate *rate_node; 1697cc7194eSJiri Pirko 1707cc7194eSJiri Pirko list_for_each_entry(rate_node, &devlink->rate_list, list) 1717cc7194eSJiri Pirko devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW); 1727cc7194eSJiri Pirko } 1737cc7194eSJiri Pirko 1747cc7194eSJiri Pirko void devlink_rates_notify_unregister(struct devlink *devlink) 1757cc7194eSJiri Pirko { 1767cc7194eSJiri Pirko struct devlink_rate *rate_node; 1777cc7194eSJiri Pirko 1787cc7194eSJiri Pirko list_for_each_entry_reverse(rate_node, &devlink->rate_list, list) 1797cc7194eSJiri Pirko devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL); 1807cc7194eSJiri Pirko } 1817cc7194eSJiri Pirko 1827cc7194eSJiri Pirko static int 1837cc7194eSJiri Pirko devlink_nl_rate_get_dump_one(struct sk_buff *msg, struct devlink *devlink, 1847cc7194eSJiri Pirko struct netlink_callback *cb, int flags) 1857cc7194eSJiri Pirko { 1867cc7194eSJiri Pirko struct devlink_nl_dump_state *state = devlink_dump_state(cb); 1877cc7194eSJiri Pirko struct devlink_rate *devlink_rate; 1887cc7194eSJiri Pirko int idx = 0; 1897cc7194eSJiri Pirko int err = 0; 1907cc7194eSJiri Pirko 1917cc7194eSJiri Pirko list_for_each_entry(devlink_rate, &devlink->rate_list, list) { 1927cc7194eSJiri Pirko enum devlink_command cmd = DEVLINK_CMD_RATE_NEW; 1937cc7194eSJiri Pirko u32 id = NETLINK_CB(cb->skb).portid; 1947cc7194eSJiri Pirko 1957cc7194eSJiri Pirko if (idx < state->idx) { 1967cc7194eSJiri Pirko idx++; 1977cc7194eSJiri Pirko continue; 1987cc7194eSJiri Pirko } 1997cc7194eSJiri Pirko err = devlink_nl_rate_fill(msg, devlink_rate, cmd, id, 2007cc7194eSJiri Pirko cb->nlh->nlmsg_seq, flags, NULL); 2017cc7194eSJiri Pirko if (err) { 2027cc7194eSJiri Pirko state->idx = idx; 2037cc7194eSJiri Pirko break; 2047cc7194eSJiri Pirko } 2057cc7194eSJiri Pirko idx++; 2067cc7194eSJiri Pirko } 2077cc7194eSJiri Pirko 2087cc7194eSJiri Pirko return err; 2097cc7194eSJiri Pirko } 2107cc7194eSJiri Pirko 2117cc7194eSJiri Pirko int devlink_nl_rate_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 2127cc7194eSJiri Pirko { 2137cc7194eSJiri Pirko return devlink_nl_dumpit(skb, cb, devlink_nl_rate_get_dump_one); 2147cc7194eSJiri Pirko } 2157cc7194eSJiri Pirko 2167cc7194eSJiri Pirko int devlink_nl_rate_get_doit(struct sk_buff *skb, struct genl_info *info) 2177cc7194eSJiri Pirko { 2187cc7194eSJiri Pirko struct devlink *devlink = info->user_ptr[0]; 2197cc7194eSJiri Pirko struct devlink_rate *devlink_rate; 2207cc7194eSJiri Pirko struct sk_buff *msg; 2217cc7194eSJiri Pirko int err; 2227cc7194eSJiri Pirko 2237cc7194eSJiri Pirko devlink_rate = devlink_rate_get_from_info(devlink, info); 2247cc7194eSJiri Pirko if (IS_ERR(devlink_rate)) 2257cc7194eSJiri Pirko return PTR_ERR(devlink_rate); 2267cc7194eSJiri Pirko 2277cc7194eSJiri Pirko msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 2287cc7194eSJiri Pirko if (!msg) 2297cc7194eSJiri Pirko return -ENOMEM; 2307cc7194eSJiri Pirko 2317cc7194eSJiri Pirko err = devlink_nl_rate_fill(msg, devlink_rate, DEVLINK_CMD_RATE_NEW, 2327cc7194eSJiri Pirko info->snd_portid, info->snd_seq, 0, 2337cc7194eSJiri Pirko info->extack); 2347cc7194eSJiri Pirko if (err) { 2357cc7194eSJiri Pirko nlmsg_free(msg); 2367cc7194eSJiri Pirko return err; 2377cc7194eSJiri Pirko } 2387cc7194eSJiri Pirko 2397cc7194eSJiri Pirko return genlmsg_reply(msg, info); 2407cc7194eSJiri Pirko } 2417cc7194eSJiri Pirko 2427cc7194eSJiri Pirko static bool 2437cc7194eSJiri Pirko devlink_rate_is_parent_node(struct devlink_rate *devlink_rate, 2447cc7194eSJiri Pirko struct devlink_rate *parent) 2457cc7194eSJiri Pirko { 2467cc7194eSJiri Pirko while (parent) { 2477cc7194eSJiri Pirko if (parent == devlink_rate) 2487cc7194eSJiri Pirko return true; 2497cc7194eSJiri Pirko parent = parent->parent; 2507cc7194eSJiri Pirko } 2517cc7194eSJiri Pirko return false; 2527cc7194eSJiri Pirko } 2537cc7194eSJiri Pirko 2547cc7194eSJiri Pirko static int 2557cc7194eSJiri Pirko devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate, 2567cc7194eSJiri Pirko struct genl_info *info, 2577cc7194eSJiri Pirko struct nlattr *nla_parent) 2587cc7194eSJiri Pirko { 2597cc7194eSJiri Pirko struct devlink *devlink = devlink_rate->devlink; 2607cc7194eSJiri Pirko const char *parent_name = nla_data(nla_parent); 2617cc7194eSJiri Pirko const struct devlink_ops *ops = devlink->ops; 2627cc7194eSJiri Pirko size_t len = strlen(parent_name); 2637cc7194eSJiri Pirko struct devlink_rate *parent; 2647cc7194eSJiri Pirko int err = -EOPNOTSUPP; 2657cc7194eSJiri Pirko 2667cc7194eSJiri Pirko parent = devlink_rate->parent; 2677cc7194eSJiri Pirko 2687cc7194eSJiri Pirko if (parent && !len) { 2697cc7194eSJiri Pirko if (devlink_rate_is_leaf(devlink_rate)) 2707cc7194eSJiri Pirko err = ops->rate_leaf_parent_set(devlink_rate, NULL, 2717cc7194eSJiri Pirko devlink_rate->priv, NULL, 2727cc7194eSJiri Pirko info->extack); 2737cc7194eSJiri Pirko else if (devlink_rate_is_node(devlink_rate)) 2747cc7194eSJiri Pirko err = ops->rate_node_parent_set(devlink_rate, NULL, 2757cc7194eSJiri Pirko devlink_rate->priv, NULL, 2767cc7194eSJiri Pirko info->extack); 2777cc7194eSJiri Pirko if (err) 2787cc7194eSJiri Pirko return err; 2797cc7194eSJiri Pirko 2807cc7194eSJiri Pirko refcount_dec(&parent->refcnt); 2817cc7194eSJiri Pirko devlink_rate->parent = NULL; 2827cc7194eSJiri Pirko } else if (len) { 2837cc7194eSJiri Pirko parent = devlink_rate_node_get_by_name(devlink, parent_name); 2847cc7194eSJiri Pirko if (IS_ERR(parent)) 2857cc7194eSJiri Pirko return -ENODEV; 2867cc7194eSJiri Pirko 2877cc7194eSJiri Pirko if (parent == devlink_rate) { 2887cc7194eSJiri Pirko NL_SET_ERR_MSG(info->extack, "Parent to self is not allowed"); 2897cc7194eSJiri Pirko return -EINVAL; 2907cc7194eSJiri Pirko } 2917cc7194eSJiri Pirko 2927cc7194eSJiri Pirko if (devlink_rate_is_node(devlink_rate) && 2937cc7194eSJiri Pirko devlink_rate_is_parent_node(devlink_rate, parent->parent)) { 2947cc7194eSJiri Pirko NL_SET_ERR_MSG(info->extack, "Node is already a parent of parent node."); 2957cc7194eSJiri Pirko return -EEXIST; 2967cc7194eSJiri Pirko } 2977cc7194eSJiri Pirko 2987cc7194eSJiri Pirko if (devlink_rate_is_leaf(devlink_rate)) 2997cc7194eSJiri Pirko err = ops->rate_leaf_parent_set(devlink_rate, parent, 3007cc7194eSJiri Pirko devlink_rate->priv, parent->priv, 3017cc7194eSJiri Pirko info->extack); 3027cc7194eSJiri Pirko else if (devlink_rate_is_node(devlink_rate)) 3037cc7194eSJiri Pirko err = ops->rate_node_parent_set(devlink_rate, parent, 3047cc7194eSJiri Pirko devlink_rate->priv, parent->priv, 3057cc7194eSJiri Pirko info->extack); 3067cc7194eSJiri Pirko if (err) 3077cc7194eSJiri Pirko return err; 3087cc7194eSJiri Pirko 3097cc7194eSJiri Pirko if (devlink_rate->parent) 3107cc7194eSJiri Pirko /* we're reassigning to other parent in this case */ 3117cc7194eSJiri Pirko refcount_dec(&devlink_rate->parent->refcnt); 3127cc7194eSJiri Pirko 3137cc7194eSJiri Pirko refcount_inc(&parent->refcnt); 3147cc7194eSJiri Pirko devlink_rate->parent = parent; 3157cc7194eSJiri Pirko } 3167cc7194eSJiri Pirko 3177cc7194eSJiri Pirko return 0; 3187cc7194eSJiri Pirko } 3197cc7194eSJiri Pirko 3207cc7194eSJiri Pirko static int devlink_nl_rate_set(struct devlink_rate *devlink_rate, 3217cc7194eSJiri Pirko const struct devlink_ops *ops, 3227cc7194eSJiri Pirko struct genl_info *info) 3237cc7194eSJiri Pirko { 3247cc7194eSJiri Pirko struct nlattr *nla_parent, **attrs = info->attrs; 3257cc7194eSJiri Pirko int err = -EOPNOTSUPP; 3267cc7194eSJiri Pirko u32 priority; 3277cc7194eSJiri Pirko u32 weight; 3287cc7194eSJiri Pirko u64 rate; 3297cc7194eSJiri Pirko 3307cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_SHARE]) { 3317cc7194eSJiri Pirko rate = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_SHARE]); 3327cc7194eSJiri Pirko if (devlink_rate_is_leaf(devlink_rate)) 3337cc7194eSJiri Pirko err = ops->rate_leaf_tx_share_set(devlink_rate, devlink_rate->priv, 3347cc7194eSJiri Pirko rate, info->extack); 3357cc7194eSJiri Pirko else if (devlink_rate_is_node(devlink_rate)) 3367cc7194eSJiri Pirko err = ops->rate_node_tx_share_set(devlink_rate, devlink_rate->priv, 3377cc7194eSJiri Pirko rate, info->extack); 3387cc7194eSJiri Pirko if (err) 3397cc7194eSJiri Pirko return err; 3407cc7194eSJiri Pirko devlink_rate->tx_share = rate; 3417cc7194eSJiri Pirko } 3427cc7194eSJiri Pirko 3437cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_MAX]) { 3447cc7194eSJiri Pirko rate = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_MAX]); 3457cc7194eSJiri Pirko if (devlink_rate_is_leaf(devlink_rate)) 3467cc7194eSJiri Pirko err = ops->rate_leaf_tx_max_set(devlink_rate, devlink_rate->priv, 3477cc7194eSJiri Pirko rate, info->extack); 3487cc7194eSJiri Pirko else if (devlink_rate_is_node(devlink_rate)) 3497cc7194eSJiri Pirko err = ops->rate_node_tx_max_set(devlink_rate, devlink_rate->priv, 3507cc7194eSJiri Pirko rate, info->extack); 3517cc7194eSJiri Pirko if (err) 3527cc7194eSJiri Pirko return err; 3537cc7194eSJiri Pirko devlink_rate->tx_max = rate; 3547cc7194eSJiri Pirko } 3557cc7194eSJiri Pirko 3567cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]) { 3577cc7194eSJiri Pirko priority = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]); 3587cc7194eSJiri Pirko if (devlink_rate_is_leaf(devlink_rate)) 3597cc7194eSJiri Pirko err = ops->rate_leaf_tx_priority_set(devlink_rate, devlink_rate->priv, 3607cc7194eSJiri Pirko priority, info->extack); 3617cc7194eSJiri Pirko else if (devlink_rate_is_node(devlink_rate)) 3627cc7194eSJiri Pirko err = ops->rate_node_tx_priority_set(devlink_rate, devlink_rate->priv, 3637cc7194eSJiri Pirko priority, info->extack); 3647cc7194eSJiri Pirko 3657cc7194eSJiri Pirko if (err) 3667cc7194eSJiri Pirko return err; 3677cc7194eSJiri Pirko devlink_rate->tx_priority = priority; 3687cc7194eSJiri Pirko } 3697cc7194eSJiri Pirko 3707cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]) { 3717cc7194eSJiri Pirko weight = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]); 3727cc7194eSJiri Pirko if (devlink_rate_is_leaf(devlink_rate)) 3737cc7194eSJiri Pirko err = ops->rate_leaf_tx_weight_set(devlink_rate, devlink_rate->priv, 3747cc7194eSJiri Pirko weight, info->extack); 3757cc7194eSJiri Pirko else if (devlink_rate_is_node(devlink_rate)) 3767cc7194eSJiri Pirko err = ops->rate_node_tx_weight_set(devlink_rate, devlink_rate->priv, 3777cc7194eSJiri Pirko weight, info->extack); 3787cc7194eSJiri Pirko 3797cc7194eSJiri Pirko if (err) 3807cc7194eSJiri Pirko return err; 3817cc7194eSJiri Pirko devlink_rate->tx_weight = weight; 3827cc7194eSJiri Pirko } 3837cc7194eSJiri Pirko 3847cc7194eSJiri Pirko nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]; 3857cc7194eSJiri Pirko if (nla_parent) { 3867cc7194eSJiri Pirko err = devlink_nl_rate_parent_node_set(devlink_rate, info, 3877cc7194eSJiri Pirko nla_parent); 3887cc7194eSJiri Pirko if (err) 3897cc7194eSJiri Pirko return err; 3907cc7194eSJiri Pirko } 3917cc7194eSJiri Pirko 3927cc7194eSJiri Pirko return 0; 3937cc7194eSJiri Pirko } 3947cc7194eSJiri Pirko 3957cc7194eSJiri Pirko static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops, 3967cc7194eSJiri Pirko struct genl_info *info, 3977cc7194eSJiri Pirko enum devlink_rate_type type) 3987cc7194eSJiri Pirko { 3997cc7194eSJiri Pirko struct nlattr **attrs = info->attrs; 4007cc7194eSJiri Pirko 4017cc7194eSJiri Pirko if (type == DEVLINK_RATE_TYPE_LEAF) { 4027cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_leaf_tx_share_set) { 4037cc7194eSJiri Pirko NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the leafs"); 4047cc7194eSJiri Pirko return false; 4057cc7194eSJiri Pirko } 4067cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_leaf_tx_max_set) { 4077cc7194eSJiri Pirko NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the leafs"); 4087cc7194eSJiri Pirko return false; 4097cc7194eSJiri Pirko } 4107cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] && 4117cc7194eSJiri Pirko !ops->rate_leaf_parent_set) { 4127cc7194eSJiri Pirko NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the leafs"); 4137cc7194eSJiri Pirko return false; 4147cc7194eSJiri Pirko } 4157cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_leaf_tx_priority_set) { 4167cc7194eSJiri Pirko NL_SET_ERR_MSG_ATTR(info->extack, 4177cc7194eSJiri Pirko attrs[DEVLINK_ATTR_RATE_TX_PRIORITY], 4187cc7194eSJiri Pirko "TX priority set isn't supported for the leafs"); 4197cc7194eSJiri Pirko return false; 4207cc7194eSJiri Pirko } 4217cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_leaf_tx_weight_set) { 4227cc7194eSJiri Pirko NL_SET_ERR_MSG_ATTR(info->extack, 4237cc7194eSJiri Pirko attrs[DEVLINK_ATTR_RATE_TX_WEIGHT], 4247cc7194eSJiri Pirko "TX weight set isn't supported for the leafs"); 4257cc7194eSJiri Pirko return false; 4267cc7194eSJiri Pirko } 4277cc7194eSJiri Pirko } else if (type == DEVLINK_RATE_TYPE_NODE) { 4287cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_node_tx_share_set) { 4297cc7194eSJiri Pirko NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the nodes"); 4307cc7194eSJiri Pirko return false; 4317cc7194eSJiri Pirko } 4327cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_node_tx_max_set) { 4337cc7194eSJiri Pirko NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the nodes"); 4347cc7194eSJiri Pirko return false; 4357cc7194eSJiri Pirko } 4367cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] && 4377cc7194eSJiri Pirko !ops->rate_node_parent_set) { 4387cc7194eSJiri Pirko NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the nodes"); 4397cc7194eSJiri Pirko return false; 4407cc7194eSJiri Pirko } 4417cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_node_tx_priority_set) { 4427cc7194eSJiri Pirko NL_SET_ERR_MSG_ATTR(info->extack, 4437cc7194eSJiri Pirko attrs[DEVLINK_ATTR_RATE_TX_PRIORITY], 4447cc7194eSJiri Pirko "TX priority set isn't supported for the nodes"); 4457cc7194eSJiri Pirko return false; 4467cc7194eSJiri Pirko } 4477cc7194eSJiri Pirko if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_node_tx_weight_set) { 4487cc7194eSJiri Pirko NL_SET_ERR_MSG_ATTR(info->extack, 4497cc7194eSJiri Pirko attrs[DEVLINK_ATTR_RATE_TX_WEIGHT], 4507cc7194eSJiri Pirko "TX weight set isn't supported for the nodes"); 4517cc7194eSJiri Pirko return false; 4527cc7194eSJiri Pirko } 4537cc7194eSJiri Pirko } else { 4547cc7194eSJiri Pirko WARN(1, "Unknown type of rate object"); 4557cc7194eSJiri Pirko return false; 4567cc7194eSJiri Pirko } 4577cc7194eSJiri Pirko 4587cc7194eSJiri Pirko return true; 4597cc7194eSJiri Pirko } 4607cc7194eSJiri Pirko 46153590934SJiri Pirko int devlink_nl_rate_set_doit(struct sk_buff *skb, struct genl_info *info) 4627cc7194eSJiri Pirko { 4637cc7194eSJiri Pirko struct devlink *devlink = info->user_ptr[0]; 4647cc7194eSJiri Pirko struct devlink_rate *devlink_rate; 4657cc7194eSJiri Pirko const struct devlink_ops *ops; 4667cc7194eSJiri Pirko int err; 4677cc7194eSJiri Pirko 4687cc7194eSJiri Pirko devlink_rate = devlink_rate_get_from_info(devlink, info); 4697cc7194eSJiri Pirko if (IS_ERR(devlink_rate)) 4707cc7194eSJiri Pirko return PTR_ERR(devlink_rate); 4717cc7194eSJiri Pirko 4727cc7194eSJiri Pirko ops = devlink->ops; 4737cc7194eSJiri Pirko if (!ops || !devlink_rate_set_ops_supported(ops, info, devlink_rate->type)) 4747cc7194eSJiri Pirko return -EOPNOTSUPP; 4757cc7194eSJiri Pirko 4767cc7194eSJiri Pirko err = devlink_nl_rate_set(devlink_rate, ops, info); 4777cc7194eSJiri Pirko 4787cc7194eSJiri Pirko if (!err) 4797cc7194eSJiri Pirko devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW); 4807cc7194eSJiri Pirko return err; 4817cc7194eSJiri Pirko } 4827cc7194eSJiri Pirko 48353590934SJiri Pirko int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info) 4847cc7194eSJiri Pirko { 4857cc7194eSJiri Pirko struct devlink *devlink = info->user_ptr[0]; 4867cc7194eSJiri Pirko struct devlink_rate *rate_node; 4877cc7194eSJiri Pirko const struct devlink_ops *ops; 4887cc7194eSJiri Pirko int err; 4897cc7194eSJiri Pirko 4907cc7194eSJiri Pirko ops = devlink->ops; 4917cc7194eSJiri Pirko if (!ops || !ops->rate_node_new || !ops->rate_node_del) { 4927cc7194eSJiri Pirko NL_SET_ERR_MSG(info->extack, "Rate nodes aren't supported"); 4937cc7194eSJiri Pirko return -EOPNOTSUPP; 4947cc7194eSJiri Pirko } 4957cc7194eSJiri Pirko 4967cc7194eSJiri Pirko if (!devlink_rate_set_ops_supported(ops, info, DEVLINK_RATE_TYPE_NODE)) 4977cc7194eSJiri Pirko return -EOPNOTSUPP; 4987cc7194eSJiri Pirko 4997cc7194eSJiri Pirko rate_node = devlink_rate_node_get_from_attrs(devlink, info->attrs); 5007cc7194eSJiri Pirko if (!IS_ERR(rate_node)) 5017cc7194eSJiri Pirko return -EEXIST; 5027cc7194eSJiri Pirko else if (rate_node == ERR_PTR(-EINVAL)) 5037cc7194eSJiri Pirko return -EINVAL; 5047cc7194eSJiri Pirko 5057cc7194eSJiri Pirko rate_node = kzalloc(sizeof(*rate_node), GFP_KERNEL); 5067cc7194eSJiri Pirko if (!rate_node) 5077cc7194eSJiri Pirko return -ENOMEM; 5087cc7194eSJiri Pirko 5097cc7194eSJiri Pirko rate_node->devlink = devlink; 5107cc7194eSJiri Pirko rate_node->type = DEVLINK_RATE_TYPE_NODE; 5117cc7194eSJiri Pirko rate_node->name = nla_strdup(info->attrs[DEVLINK_ATTR_RATE_NODE_NAME], GFP_KERNEL); 5127cc7194eSJiri Pirko if (!rate_node->name) { 5137cc7194eSJiri Pirko err = -ENOMEM; 5147cc7194eSJiri Pirko goto err_strdup; 5157cc7194eSJiri Pirko } 5167cc7194eSJiri Pirko 5177cc7194eSJiri Pirko err = ops->rate_node_new(rate_node, &rate_node->priv, info->extack); 5187cc7194eSJiri Pirko if (err) 5197cc7194eSJiri Pirko goto err_node_new; 5207cc7194eSJiri Pirko 5217cc7194eSJiri Pirko err = devlink_nl_rate_set(rate_node, ops, info); 5227cc7194eSJiri Pirko if (err) 5237cc7194eSJiri Pirko goto err_rate_set; 5247cc7194eSJiri Pirko 5257cc7194eSJiri Pirko refcount_set(&rate_node->refcnt, 1); 5267cc7194eSJiri Pirko list_add(&rate_node->list, &devlink->rate_list); 5277cc7194eSJiri Pirko devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW); 5287cc7194eSJiri Pirko return 0; 5297cc7194eSJiri Pirko 5307cc7194eSJiri Pirko err_rate_set: 5317cc7194eSJiri Pirko ops->rate_node_del(rate_node, rate_node->priv, info->extack); 5327cc7194eSJiri Pirko err_node_new: 5337cc7194eSJiri Pirko kfree(rate_node->name); 5347cc7194eSJiri Pirko err_strdup: 5357cc7194eSJiri Pirko kfree(rate_node); 5367cc7194eSJiri Pirko return err; 5377cc7194eSJiri Pirko } 5387cc7194eSJiri Pirko 53953590934SJiri Pirko int devlink_nl_rate_del_doit(struct sk_buff *skb, struct genl_info *info) 5407cc7194eSJiri Pirko { 5417cc7194eSJiri Pirko struct devlink *devlink = info->user_ptr[0]; 5427cc7194eSJiri Pirko struct devlink_rate *rate_node; 5437cc7194eSJiri Pirko int err; 5447cc7194eSJiri Pirko 5457cc7194eSJiri Pirko rate_node = devlink_rate_node_get_from_info(devlink, info); 5467cc7194eSJiri Pirko if (IS_ERR(rate_node)) 5477cc7194eSJiri Pirko return PTR_ERR(rate_node); 5487cc7194eSJiri Pirko 5497cc7194eSJiri Pirko if (refcount_read(&rate_node->refcnt) > 1) { 5507cc7194eSJiri Pirko NL_SET_ERR_MSG(info->extack, "Node has children. Cannot delete node."); 5517cc7194eSJiri Pirko return -EBUSY; 5527cc7194eSJiri Pirko } 5537cc7194eSJiri Pirko 5547cc7194eSJiri Pirko devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL); 5557cc7194eSJiri Pirko err = devlink->ops->rate_node_del(rate_node, rate_node->priv, 5567cc7194eSJiri Pirko info->extack); 5577cc7194eSJiri Pirko if (rate_node->parent) 5587cc7194eSJiri Pirko refcount_dec(&rate_node->parent->refcnt); 5597cc7194eSJiri Pirko list_del(&rate_node->list); 5607cc7194eSJiri Pirko kfree(rate_node->name); 5617cc7194eSJiri Pirko kfree(rate_node); 5627cc7194eSJiri Pirko return err; 5637cc7194eSJiri Pirko } 5647cc7194eSJiri Pirko 5657cc7194eSJiri Pirko int devlink_rate_nodes_check(struct devlink *devlink, u16 mode, 5667cc7194eSJiri Pirko struct netlink_ext_ack *extack) 5677cc7194eSJiri Pirko { 5687cc7194eSJiri Pirko struct devlink_rate *devlink_rate; 5697cc7194eSJiri Pirko 5707cc7194eSJiri Pirko list_for_each_entry(devlink_rate, &devlink->rate_list, list) 5717cc7194eSJiri Pirko if (devlink_rate_is_node(devlink_rate)) { 5727cc7194eSJiri Pirko NL_SET_ERR_MSG(extack, "Rate node(s) exists."); 5737cc7194eSJiri Pirko return -EBUSY; 5747cc7194eSJiri Pirko } 5757cc7194eSJiri Pirko return 0; 5767cc7194eSJiri Pirko } 5777cc7194eSJiri Pirko 5787cc7194eSJiri Pirko /** 5797cc7194eSJiri Pirko * devl_rate_node_create - create devlink rate node 5807cc7194eSJiri Pirko * @devlink: devlink instance 5817cc7194eSJiri Pirko * @priv: driver private data 5827cc7194eSJiri Pirko * @node_name: name of the resulting node 5837cc7194eSJiri Pirko * @parent: parent devlink_rate struct 5847cc7194eSJiri Pirko * 5857cc7194eSJiri Pirko * Create devlink rate object of type node 5867cc7194eSJiri Pirko */ 5877cc7194eSJiri Pirko struct devlink_rate * 5887cc7194eSJiri Pirko devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, 5897cc7194eSJiri Pirko struct devlink_rate *parent) 5907cc7194eSJiri Pirko { 5917cc7194eSJiri Pirko struct devlink_rate *rate_node; 5927cc7194eSJiri Pirko 5937cc7194eSJiri Pirko rate_node = devlink_rate_node_get_by_name(devlink, node_name); 5947cc7194eSJiri Pirko if (!IS_ERR(rate_node)) 5957cc7194eSJiri Pirko return ERR_PTR(-EEXIST); 5967cc7194eSJiri Pirko 5977cc7194eSJiri Pirko rate_node = kzalloc(sizeof(*rate_node), GFP_KERNEL); 5987cc7194eSJiri Pirko if (!rate_node) 5997cc7194eSJiri Pirko return ERR_PTR(-ENOMEM); 6007cc7194eSJiri Pirko 6017cc7194eSJiri Pirko if (parent) { 6027cc7194eSJiri Pirko rate_node->parent = parent; 6037cc7194eSJiri Pirko refcount_inc(&rate_node->parent->refcnt); 6047cc7194eSJiri Pirko } 6057cc7194eSJiri Pirko 6067cc7194eSJiri Pirko rate_node->type = DEVLINK_RATE_TYPE_NODE; 6077cc7194eSJiri Pirko rate_node->devlink = devlink; 6087cc7194eSJiri Pirko rate_node->priv = priv; 6097cc7194eSJiri Pirko 6107cc7194eSJiri Pirko rate_node->name = kstrdup(node_name, GFP_KERNEL); 6117cc7194eSJiri Pirko if (!rate_node->name) { 6127cc7194eSJiri Pirko kfree(rate_node); 6137cc7194eSJiri Pirko return ERR_PTR(-ENOMEM); 6147cc7194eSJiri Pirko } 6157cc7194eSJiri Pirko 6167cc7194eSJiri Pirko refcount_set(&rate_node->refcnt, 1); 6177cc7194eSJiri Pirko list_add(&rate_node->list, &devlink->rate_list); 6187cc7194eSJiri Pirko devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW); 6197cc7194eSJiri Pirko return rate_node; 6207cc7194eSJiri Pirko } 6217cc7194eSJiri Pirko EXPORT_SYMBOL_GPL(devl_rate_node_create); 6227cc7194eSJiri Pirko 6237cc7194eSJiri Pirko /** 6247cc7194eSJiri Pirko * devl_rate_leaf_create - create devlink rate leaf 6257cc7194eSJiri Pirko * @devlink_port: devlink port object to create rate object on 6267cc7194eSJiri Pirko * @priv: driver private data 6277cc7194eSJiri Pirko * @parent: parent devlink_rate struct 6287cc7194eSJiri Pirko * 6297cc7194eSJiri Pirko * Create devlink rate object of type leaf on provided @devlink_port. 6307cc7194eSJiri Pirko */ 6317cc7194eSJiri Pirko int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv, 6327cc7194eSJiri Pirko struct devlink_rate *parent) 6337cc7194eSJiri Pirko { 6347cc7194eSJiri Pirko struct devlink *devlink = devlink_port->devlink; 6357cc7194eSJiri Pirko struct devlink_rate *devlink_rate; 6367cc7194eSJiri Pirko 6377cc7194eSJiri Pirko devl_assert_locked(devlink_port->devlink); 6387cc7194eSJiri Pirko 6397cc7194eSJiri Pirko if (WARN_ON(devlink_port->devlink_rate)) 6407cc7194eSJiri Pirko return -EBUSY; 6417cc7194eSJiri Pirko 6427cc7194eSJiri Pirko devlink_rate = kzalloc(sizeof(*devlink_rate), GFP_KERNEL); 6437cc7194eSJiri Pirko if (!devlink_rate) 6447cc7194eSJiri Pirko return -ENOMEM; 6457cc7194eSJiri Pirko 6467cc7194eSJiri Pirko if (parent) { 6477cc7194eSJiri Pirko devlink_rate->parent = parent; 6487cc7194eSJiri Pirko refcount_inc(&devlink_rate->parent->refcnt); 6497cc7194eSJiri Pirko } 6507cc7194eSJiri Pirko 6517cc7194eSJiri Pirko devlink_rate->type = DEVLINK_RATE_TYPE_LEAF; 6527cc7194eSJiri Pirko devlink_rate->devlink = devlink; 6537cc7194eSJiri Pirko devlink_rate->devlink_port = devlink_port; 6547cc7194eSJiri Pirko devlink_rate->priv = priv; 6557cc7194eSJiri Pirko list_add_tail(&devlink_rate->list, &devlink->rate_list); 6567cc7194eSJiri Pirko devlink_port->devlink_rate = devlink_rate; 6577cc7194eSJiri Pirko devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW); 6587cc7194eSJiri Pirko 6597cc7194eSJiri Pirko return 0; 6607cc7194eSJiri Pirko } 6617cc7194eSJiri Pirko EXPORT_SYMBOL_GPL(devl_rate_leaf_create); 6627cc7194eSJiri Pirko 6637cc7194eSJiri Pirko /** 6647cc7194eSJiri Pirko * devl_rate_leaf_destroy - destroy devlink rate leaf 6657cc7194eSJiri Pirko * 6667cc7194eSJiri Pirko * @devlink_port: devlink port linked to the rate object 6677cc7194eSJiri Pirko * 6687cc7194eSJiri Pirko * Destroy the devlink rate object of type leaf on provided @devlink_port. 6697cc7194eSJiri Pirko */ 6707cc7194eSJiri Pirko void devl_rate_leaf_destroy(struct devlink_port *devlink_port) 6717cc7194eSJiri Pirko { 6727cc7194eSJiri Pirko struct devlink_rate *devlink_rate = devlink_port->devlink_rate; 6737cc7194eSJiri Pirko 6747cc7194eSJiri Pirko devl_assert_locked(devlink_port->devlink); 6757cc7194eSJiri Pirko if (!devlink_rate) 6767cc7194eSJiri Pirko return; 6777cc7194eSJiri Pirko 6787cc7194eSJiri Pirko devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_DEL); 6797cc7194eSJiri Pirko if (devlink_rate->parent) 6807cc7194eSJiri Pirko refcount_dec(&devlink_rate->parent->refcnt); 6817cc7194eSJiri Pirko list_del(&devlink_rate->list); 6827cc7194eSJiri Pirko devlink_port->devlink_rate = NULL; 6837cc7194eSJiri Pirko kfree(devlink_rate); 6847cc7194eSJiri Pirko } 6857cc7194eSJiri Pirko EXPORT_SYMBOL_GPL(devl_rate_leaf_destroy); 6867cc7194eSJiri Pirko 6877cc7194eSJiri Pirko /** 6887cc7194eSJiri Pirko * devl_rate_nodes_destroy - destroy all devlink rate nodes on device 6897cc7194eSJiri Pirko * @devlink: devlink instance 6907cc7194eSJiri Pirko * 6917cc7194eSJiri Pirko * Unset parent for all rate objects and destroy all rate nodes 6927cc7194eSJiri Pirko * on specified device. 6937cc7194eSJiri Pirko */ 6947cc7194eSJiri Pirko void devl_rate_nodes_destroy(struct devlink *devlink) 6957cc7194eSJiri Pirko { 6967cc7194eSJiri Pirko static struct devlink_rate *devlink_rate, *tmp; 6977cc7194eSJiri Pirko const struct devlink_ops *ops = devlink->ops; 6987cc7194eSJiri Pirko 6997cc7194eSJiri Pirko devl_assert_locked(devlink); 7007cc7194eSJiri Pirko 7017cc7194eSJiri Pirko list_for_each_entry(devlink_rate, &devlink->rate_list, list) { 7027cc7194eSJiri Pirko if (!devlink_rate->parent) 7037cc7194eSJiri Pirko continue; 7047cc7194eSJiri Pirko 7057cc7194eSJiri Pirko refcount_dec(&devlink_rate->parent->refcnt); 7067cc7194eSJiri Pirko if (devlink_rate_is_leaf(devlink_rate)) 7077cc7194eSJiri Pirko ops->rate_leaf_parent_set(devlink_rate, NULL, devlink_rate->priv, 7087cc7194eSJiri Pirko NULL, NULL); 7097cc7194eSJiri Pirko else if (devlink_rate_is_node(devlink_rate)) 7107cc7194eSJiri Pirko ops->rate_node_parent_set(devlink_rate, NULL, devlink_rate->priv, 7117cc7194eSJiri Pirko NULL, NULL); 7127cc7194eSJiri Pirko } 7137cc7194eSJiri Pirko list_for_each_entry_safe(devlink_rate, tmp, &devlink->rate_list, list) { 7147cc7194eSJiri Pirko if (devlink_rate_is_node(devlink_rate)) { 7157cc7194eSJiri Pirko ops->rate_node_del(devlink_rate, devlink_rate->priv, NULL); 7167cc7194eSJiri Pirko list_del(&devlink_rate->list); 7177cc7194eSJiri Pirko kfree(devlink_rate->name); 7187cc7194eSJiri Pirko kfree(devlink_rate); 7197cc7194eSJiri Pirko } 7207cc7194eSJiri Pirko } 7217cc7194eSJiri Pirko } 7227cc7194eSJiri Pirko EXPORT_SYMBOL_GPL(devl_rate_nodes_destroy); 723