return ice_devlink_port_split(devlink, port, 1, extack);
 }
 
+/**
+ * ice_traverse_tx_tree - traverse Tx scheduler tree
+ * @devlink: devlink struct
+ * @node: current node, used for recursion
+ * @tc_node: tc_node struct, that is treated as a root
+ * @pf: pf struct
+ *
+ * This function traverses Tx scheduler tree and exports
+ * entire structure to the devlink-rate.
+ */
+static void ice_traverse_tx_tree(struct devlink *devlink, struct ice_sched_node *node,
+                                struct ice_sched_node *tc_node, struct ice_pf *pf)
+{
+       struct devlink_rate *rate_node = NULL;
+       struct ice_vf *vf;
+       int i;
+
+       if (node->parent == tc_node) {
+               /* create root node */
+               rate_node = devl_rate_node_create(devlink, node, node->name, NULL);
+       } else if (node->vsi_handle &&
+                  pf->vsi[node->vsi_handle]->vf) {
+               vf = pf->vsi[node->vsi_handle]->vf;
+               if (!vf->devlink_port.devlink_rate)
+                       /* leaf nodes doesn't have children
+                        * so we don't set rate_node
+                        */
+                       devl_rate_leaf_create(&vf->devlink_port, node,
+                                             node->parent->rate_node);
+       } else if (node->info.data.elem_type != ICE_AQC_ELEM_TYPE_LEAF &&
+                  node->parent->rate_node) {
+               rate_node = devl_rate_node_create(devlink, node, node->name,
+                                                 node->parent->rate_node);
+       }
+
+       if (rate_node && !IS_ERR(rate_node))
+               node->rate_node = rate_node;
+
+       for (i = 0; i < node->num_children; i++)
+               ice_traverse_tx_tree(devlink, node->children[i], tc_node, pf);
+}
+
+/**
+ * ice_devlink_rate_init_tx_topology - export Tx scheduler tree to devlink rate
+ * @devlink: devlink struct
+ * @vsi: main vsi struct
+ *
+ * This function finds a root node, then calls ice_traverse_tx tree, which
+ * traverses the tree and exports it's contents to devlink rate.
+ */
+int ice_devlink_rate_init_tx_topology(struct devlink *devlink, struct ice_vsi *vsi)
+{
+       struct ice_port_info *pi = vsi->port_info;
+       struct ice_sched_node *tc_node;
+       struct ice_pf *pf = vsi->back;
+       int i;
+
+       tc_node = pi->root->children[0];
+       mutex_lock(&pi->sched_lock);
+       devl_lock(devlink);
+       for (i = 0; i < tc_node->num_children; i++)
+               ice_traverse_tx_tree(devlink, tc_node->children[i], tc_node, pf);
+       devl_unlock(devlink);
+       mutex_unlock(&pi->sched_lock);
+
+       return 0;
+}
+
+/**
+ * ice_set_object_tx_share - sets node scheduling parameter
+ * @pi: devlink struct instance
+ * @node: node struct instance
+ * @bw: bandwidth in bytes per second
+ * @extack: extended netdev ack structure
+ *
+ * This function sets ICE_MIN_BW scheduling BW limit.
+ */
+static int ice_set_object_tx_share(struct ice_port_info *pi, struct ice_sched_node *node,
+                                  u64 bw, struct netlink_ext_ack *extack)
+{
+       int status;
+
+       mutex_lock(&pi->sched_lock);
+       /* converts bytes per second to kilo bits per second */
+       node->tx_share = div_u64(bw, 125);
+       status = ice_sched_set_node_bw_lmt(pi, node, ICE_MIN_BW, node->tx_share);
+       mutex_unlock(&pi->sched_lock);
+
+       if (status)
+               NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_share");
+
+       return status;
+}
+
+/**
+ * ice_set_object_tx_max - sets node scheduling parameter
+ * @pi: devlink struct instance
+ * @node: node struct instance
+ * @bw: bandwidth in bytes per second
+ * @extack: extended netdev ack structure
+ *
+ * This function sets ICE_MAX_BW scheduling BW limit.
+ */
+static int ice_set_object_tx_max(struct ice_port_info *pi, struct ice_sched_node *node,
+                                u64 bw, struct netlink_ext_ack *extack)
+{
+       int status;
+
+       mutex_lock(&pi->sched_lock);
+       /* converts bytes per second value to kilo bits per second */
+       node->tx_max = div_u64(bw, 125);
+       status = ice_sched_set_node_bw_lmt(pi, node, ICE_MAX_BW, node->tx_max);
+       mutex_unlock(&pi->sched_lock);
+
+       if (status)
+               NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_max");
+
+       return status;
+}
+
+/**
+ * ice_set_object_tx_priority - sets node scheduling parameter
+ * @pi: devlink struct instance
+ * @node: node struct instance
+ * @priority: value representing priority for strict priority arbitration
+ * @extack: extended netdev ack structure
+ *
+ * This function sets priority of node among siblings.
+ */
+static int ice_set_object_tx_priority(struct ice_port_info *pi, struct ice_sched_node *node,
+                                     u32 priority, struct netlink_ext_ack *extack)
+{
+       int status;
+
+       if (node->tx_priority >= 8) {
+               NL_SET_ERR_MSG_MOD(extack, "Priority should be less than 8");
+               return -EINVAL;
+       }
+
+       mutex_lock(&pi->sched_lock);
+       node->tx_priority = priority;
+       status = ice_sched_set_node_priority(pi, node, node->tx_priority);
+       mutex_unlock(&pi->sched_lock);
+
+       if (status)
+               NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_priority");
+
+       return status;
+}
+
+/**
+ * ice_set_object_tx_weight - sets node scheduling parameter
+ * @pi: devlink struct instance
+ * @node: node struct instance
+ * @weight: value represeting relative weight for WFQ arbitration
+ * @extack: extended netdev ack structure
+ *
+ * This function sets node weight for WFQ algorithm.
+ */
+static int ice_set_object_tx_weight(struct ice_port_info *pi, struct ice_sched_node *node,
+                                   u32 weight, struct netlink_ext_ack *extack)
+{
+       int status;
+
+       if (node->tx_weight > 200 || node->tx_weight < 1) {
+               NL_SET_ERR_MSG_MOD(extack, "Weight must be between 1 and 200");
+               return -EINVAL;
+       }
+
+       mutex_lock(&pi->sched_lock);
+       node->tx_weight = weight;
+       status = ice_sched_set_node_weight(pi, node, node->tx_weight);
+       mutex_unlock(&pi->sched_lock);
+
+       if (status)
+               NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_weight");
+
+       return status;
+}
+
+/**
+ * ice_get_pi_from_dev_rate - get port info from devlink_rate
+ * @rate_node: devlink struct instance
+ *
+ * This function returns corresponding port_info struct of devlink_rate
+ */
+static struct ice_port_info *ice_get_pi_from_dev_rate(struct devlink_rate *rate_node)
+{
+       struct ice_pf *pf = devlink_priv(rate_node->devlink);
+
+       return ice_get_main_vsi(pf)->port_info;
+}
+
+static int ice_devlink_rate_node_new(struct devlink_rate *rate_node, void **priv,
+                                    struct netlink_ext_ack *extack)
+{
+       struct ice_sched_node *node;
+       struct ice_port_info *pi;
+
+       pi = ice_get_pi_from_dev_rate(rate_node);
+
+       /* preallocate memory for ice_sched_node */
+       node = devm_kzalloc(ice_hw_to_dev(pi->hw), sizeof(*node), GFP_KERNEL);
+       *priv = node;
+
+       return 0;
+}
+
+static int ice_devlink_rate_node_del(struct devlink_rate *rate_node, void *priv,
+                                    struct netlink_ext_ack *extack)
+{
+       struct ice_sched_node *node, *tc_node;
+       struct ice_port_info *pi;
+
+       pi = ice_get_pi_from_dev_rate(rate_node);
+       tc_node = pi->root->children[0];
+       node = priv;
+
+       if (!rate_node->parent || !node || tc_node == node || !extack)
+               return 0;
+
+       /* can't allow to delete a node with children */
+       if (node->num_children)
+               return -EINVAL;
+
+       mutex_lock(&pi->sched_lock);
+       ice_free_sched_node(pi, node);
+       mutex_unlock(&pi->sched_lock);
+
+       return 0;
+}
+
+static int ice_devlink_rate_leaf_tx_max_set(struct devlink_rate *rate_leaf, void *priv,
+                                           u64 tx_max, struct netlink_ext_ack *extack)
+{
+       struct ice_sched_node *node = priv;
+
+       if (!node)
+               return 0;
+
+       return ice_set_object_tx_max(ice_get_pi_from_dev_rate(rate_leaf),
+                                    node, tx_max, extack);
+}
+
+static int ice_devlink_rate_leaf_tx_share_set(struct devlink_rate *rate_leaf, void *priv,
+                                             u64 tx_share, struct netlink_ext_ack *extack)
+{
+       struct ice_sched_node *node = priv;
+
+       if (!node)
+               return 0;
+
+       return ice_set_object_tx_share(ice_get_pi_from_dev_rate(rate_leaf), node,
+                                      tx_share, extack);
+}
+
+static int ice_devlink_rate_leaf_tx_priority_set(struct devlink_rate *rate_leaf, void *priv,
+                                                u32 tx_priority, struct netlink_ext_ack *extack)
+{
+       struct ice_sched_node *node = priv;
+
+       if (!node)
+               return 0;
+
+       return ice_set_object_tx_priority(ice_get_pi_from_dev_rate(rate_leaf), node,
+                                         tx_priority, extack);
+}
+
+static int ice_devlink_rate_leaf_tx_weight_set(struct devlink_rate *rate_leaf, void *priv,
+                                              u32 tx_weight, struct netlink_ext_ack *extack)
+{
+       struct ice_sched_node *node = priv;
+
+       if (!node)
+               return 0;
+
+       return ice_set_object_tx_weight(ice_get_pi_from_dev_rate(rate_leaf), node,
+                                       tx_weight, extack);
+}
+
+static int ice_devlink_rate_node_tx_max_set(struct devlink_rate *rate_node, void *priv,
+                                           u64 tx_max, struct netlink_ext_ack *extack)
+{
+       struct ice_sched_node *node = priv;
+
+       if (!node)
+               return 0;
+
+       return ice_set_object_tx_max(ice_get_pi_from_dev_rate(rate_node),
+                                    node, tx_max, extack);
+}
+
+static int ice_devlink_rate_node_tx_share_set(struct devlink_rate *rate_node, void *priv,
+                                             u64 tx_share, struct netlink_ext_ack *extack)
+{
+       struct ice_sched_node *node = priv;
+
+       if (!node)
+               return 0;
+
+       return ice_set_object_tx_share(ice_get_pi_from_dev_rate(rate_node),
+                                      node, tx_share, extack);
+}
+
+static int ice_devlink_rate_node_tx_priority_set(struct devlink_rate *rate_node, void *priv,
+                                                u32 tx_priority, struct netlink_ext_ack *extack)
+{
+       struct ice_sched_node *node = priv;
+
+       if (!node)
+               return 0;
+
+       return ice_set_object_tx_priority(ice_get_pi_from_dev_rate(rate_node),
+                                         node, tx_priority, extack);
+}
+
+static int ice_devlink_rate_node_tx_weight_set(struct devlink_rate *rate_node, void *priv,
+                                              u32 tx_weight, struct netlink_ext_ack *extack)
+{
+       struct ice_sched_node *node = priv;
+
+       if (!node)
+               return 0;
+
+       return ice_set_object_tx_weight(ice_get_pi_from_dev_rate(rate_node),
+                                       node, tx_weight, extack);
+}
+
+static int ice_devlink_set_parent(struct devlink_rate *devlink_rate,
+                                 struct devlink_rate *parent,
+                                 void *priv, void *parent_priv,
+                                 struct netlink_ext_ack *extack)
+{
+       struct ice_port_info *pi = ice_get_pi_from_dev_rate(devlink_rate);
+       struct ice_sched_node *tc_node, *node, *parent_node;
+       u16 num_nodes_added;
+       u32 first_node_teid;
+       u32 node_teid;
+       int status;
+
+       tc_node = pi->root->children[0];
+       node = priv;
+
+       if (!extack)
+               return 0;
+
+       if (!parent) {
+               if (!node || tc_node == node || node->num_children)
+                       return -EINVAL;
+
+               mutex_lock(&pi->sched_lock);
+               ice_free_sched_node(pi, node);
+               mutex_unlock(&pi->sched_lock);
+
+               return 0;
+       }
+
+       parent_node = parent_priv;
+
+       /* if the node doesn't exist, create it */
+       if (!node->parent) {
+               mutex_lock(&pi->sched_lock);
+               status = ice_sched_add_elems(pi, tc_node, parent_node,
+                                            parent_node->tx_sched_layer + 1,
+                                            1, &num_nodes_added, &first_node_teid,
+                                            &node);
+               mutex_unlock(&pi->sched_lock);
+
+               if (status) {
+                       NL_SET_ERR_MSG_MOD(extack, "Can't add a new node");
+                       return status;
+               }
+
+               if (devlink_rate->tx_share)
+                       ice_set_object_tx_share(pi, node, devlink_rate->tx_share, extack);
+               if (devlink_rate->tx_max)
+                       ice_set_object_tx_max(pi, node, devlink_rate->tx_max, extack);
+               if (devlink_rate->tx_priority)
+                       ice_set_object_tx_priority(pi, node, devlink_rate->tx_priority, extack);
+               if (devlink_rate->tx_weight)
+                       ice_set_object_tx_weight(pi, node, devlink_rate->tx_weight, extack);
+       } else {
+               node_teid = le32_to_cpu(node->info.node_teid);
+               mutex_lock(&pi->sched_lock);
+               status = ice_sched_move_nodes(pi, parent_node, 1, &node_teid);
+               mutex_unlock(&pi->sched_lock);
+
+               if (status)
+                       NL_SET_ERR_MSG_MOD(extack, "Can't move existing node to a new parent");
+       }
+
+       return status;
+}
+
 static const struct devlink_ops ice_devlink_ops = {
        .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
        .reload_actions = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE),
        .eswitch_mode_set = ice_eswitch_mode_set,
        .info_get = ice_devlink_info_get,
        .flash_update = ice_devlink_flash_update,
+
+       .rate_node_new = ice_devlink_rate_node_new,
+       .rate_node_del = ice_devlink_rate_node_del,
+
+       .rate_leaf_tx_max_set = ice_devlink_rate_leaf_tx_max_set,
+       .rate_leaf_tx_share_set = ice_devlink_rate_leaf_tx_share_set,
+       .rate_leaf_tx_priority_set = ice_devlink_rate_leaf_tx_priority_set,
+       .rate_leaf_tx_weight_set = ice_devlink_rate_leaf_tx_weight_set,
+
+       .rate_node_tx_max_set = ice_devlink_rate_node_tx_max_set,
+       .rate_node_tx_share_set = ice_devlink_rate_node_tx_share_set,
+       .rate_node_tx_priority_set = ice_devlink_rate_node_tx_priority_set,
+       .rate_node_tx_weight_set = ice_devlink_rate_node_tx_weight_set,
+
+       .rate_leaf_parent_set = ice_devlink_set_parent,
+       .rate_node_parent_set = ice_devlink_set_parent,
 };
 
 static int
  */
 void ice_devlink_destroy_vf_port(struct ice_vf *vf)
 {
+       devl_rate_leaf_destroy(&vf->devlink_port);
        devlink_port_unregister(&vf->devlink_port);
 }