// SPDX-License-Identifier: GPL-2.0 /* Marvell RVU Ethernet driver * * Copyright (C) 2023 Marvell. * */ #include #include #include #include #include "otx2_common.h" #include "cn10k.h" #include "qos.h" #define OTX2_QOS_QID_INNER 0xFFFFU #define OTX2_QOS_QID_NONE 0xFFFEU #define OTX2_QOS_ROOT_CLASSID 0xFFFFFFFF #define OTX2_QOS_CLASS_NONE 0 #define OTX2_QOS_DEFAULT_PRIO 0xF #define OTX2_QOS_INVALID_SQ 0xFFFF #define OTX2_QOS_INVALID_TXSCHQ_IDX 0xFFFF #define CN10K_MAX_RR_WEIGHT GENMASK_ULL(13, 0) #define OTX2_MAX_RR_QUANTUM GENMASK_ULL(23, 0) static void otx2_qos_update_tx_netdev_queues(struct otx2_nic *pfvf) { struct otx2_hw *hw = &pfvf->hw; int tx_queues, qos_txqs, err; qos_txqs = bitmap_weight(pfvf->qos.qos_sq_bmap, OTX2_QOS_MAX_LEAF_NODES); tx_queues = hw->tx_queues + qos_txqs; err = netif_set_real_num_tx_queues(pfvf->netdev, tx_queues); if (err) { netdev_err(pfvf->netdev, "Failed to set no of Tx queues: %d\n", tx_queues); return; } } static void otx2_qos_get_regaddr(struct otx2_qos_node *node, struct nix_txschq_config *cfg, int index) { if (node->level == NIX_TXSCH_LVL_SMQ) { cfg->reg[index++] = NIX_AF_MDQX_PARENT(node->schq); cfg->reg[index++] = NIX_AF_MDQX_SCHEDULE(node->schq); cfg->reg[index++] = NIX_AF_MDQX_PIR(node->schq); cfg->reg[index] = NIX_AF_MDQX_CIR(node->schq); } else if (node->level == NIX_TXSCH_LVL_TL4) { cfg->reg[index++] = NIX_AF_TL4X_PARENT(node->schq); cfg->reg[index++] = NIX_AF_TL4X_SCHEDULE(node->schq); cfg->reg[index++] = NIX_AF_TL4X_PIR(node->schq); cfg->reg[index] = NIX_AF_TL4X_CIR(node->schq); } else if (node->level == NIX_TXSCH_LVL_TL3) { cfg->reg[index++] = NIX_AF_TL3X_PARENT(node->schq); cfg->reg[index++] = NIX_AF_TL3X_SCHEDULE(node->schq); cfg->reg[index++] = NIX_AF_TL3X_PIR(node->schq); cfg->reg[index] = NIX_AF_TL3X_CIR(node->schq); } else if (node->level == NIX_TXSCH_LVL_TL2) { cfg->reg[index++] = NIX_AF_TL2X_PARENT(node->schq); cfg->reg[index++] = NIX_AF_TL2X_SCHEDULE(node->schq); cfg->reg[index++] = NIX_AF_TL2X_PIR(node->schq); cfg->reg[index] = NIX_AF_TL2X_CIR(node->schq); } } static int otx2_qos_quantum_to_dwrr_weight(struct otx2_nic *pfvf, u32 quantum) { u32 weight; weight = quantum / pfvf->hw.dwrr_mtu; if (quantum % pfvf->hw.dwrr_mtu) weight += 1; return weight; } static void otx2_config_sched_shaping(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct nix_txschq_config *cfg, int *num_regs) { u32 rr_weight; u32 quantum; u64 maxrate; otx2_qos_get_regaddr(node, cfg, *num_regs); /* configure parent txschq */ cfg->regval[*num_regs] = node->parent->schq << 16; (*num_regs)++; /* configure prio/quantum */ if (node->qid == OTX2_QOS_QID_NONE) { cfg->regval[*num_regs] = node->prio << 24 | mtu_to_dwrr_weight(pfvf, pfvf->tx_max_pktlen); (*num_regs)++; return; } /* configure priority/quantum */ if (node->is_static) { cfg->regval[*num_regs] = (node->schq - node->parent->prio_anchor) << 24; } else { quantum = node->quantum ? node->quantum : pfvf->tx_max_pktlen; rr_weight = otx2_qos_quantum_to_dwrr_weight(pfvf, quantum); cfg->regval[*num_regs] = node->parent->child_dwrr_prio << 24 | rr_weight; } (*num_regs)++; /* configure PIR */ maxrate = (node->rate > node->ceil) ? node->rate : node->ceil; cfg->regval[*num_regs] = otx2_get_txschq_rate_regval(pfvf, maxrate, 65536); (*num_regs)++; /* Don't configure CIR when both CIR+PIR not supported * On 96xx, CIR + PIR + RED_ALGO=STALL causes deadlock */ if (!test_bit(QOS_CIR_PIR_SUPPORT, &pfvf->hw.cap_flag)) return; cfg->regval[*num_regs] = otx2_get_txschq_rate_regval(pfvf, node->rate, 65536); (*num_regs)++; } static void __otx2_qos_txschq_cfg(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct nix_txschq_config *cfg) { struct otx2_hw *hw = &pfvf->hw; int num_regs = 0; u8 level; level = node->level; /* program txschq registers */ if (level == NIX_TXSCH_LVL_SMQ) { cfg->reg[num_regs] = NIX_AF_SMQX_CFG(node->schq); cfg->regval[num_regs] = ((u64)pfvf->tx_max_pktlen << 8) | OTX2_MIN_MTU; cfg->regval[num_regs] |= (0x20ULL << 51) | (0x80ULL << 39) | (0x2ULL << 36); num_regs++; otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); } else if (level == NIX_TXSCH_LVL_TL4) { otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); } else if (level == NIX_TXSCH_LVL_TL3) { /* configure link cfg */ if (level == pfvf->qos.link_cfg_lvl) { cfg->reg[num_regs] = NIX_AF_TL3_TL2X_LINKX_CFG(node->schq, hw->tx_link); cfg->regval[num_regs] = BIT_ULL(13) | BIT_ULL(12); num_regs++; } otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); } else if (level == NIX_TXSCH_LVL_TL2) { /* configure link cfg */ if (level == pfvf->qos.link_cfg_lvl) { cfg->reg[num_regs] = NIX_AF_TL3_TL2X_LINKX_CFG(node->schq, hw->tx_link); cfg->regval[num_regs] = BIT_ULL(13) | BIT_ULL(12); num_regs++; } /* check if node is root */ if (node->qid == OTX2_QOS_QID_INNER && !node->parent) { cfg->reg[num_regs] = NIX_AF_TL2X_SCHEDULE(node->schq); cfg->regval[num_regs] = TXSCH_TL1_DFLT_RR_PRIO << 24 | mtu_to_dwrr_weight(pfvf, pfvf->tx_max_pktlen); num_regs++; goto txschq_cfg_out; } otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); } txschq_cfg_out: cfg->num_regs = num_regs; } static int otx2_qos_txschq_set_parent_topology(struct otx2_nic *pfvf, struct otx2_qos_node *parent) { struct mbox *mbox = &pfvf->mbox; struct nix_txschq_config *cfg; int rc; if (parent->level == NIX_TXSCH_LVL_MDQ) return 0; mutex_lock(&mbox->lock); cfg = otx2_mbox_alloc_msg_nix_txschq_cfg(&pfvf->mbox); if (!cfg) { mutex_unlock(&mbox->lock); return -ENOMEM; } cfg->lvl = parent->level; if (parent->level == NIX_TXSCH_LVL_TL4) cfg->reg[0] = NIX_AF_TL4X_TOPOLOGY(parent->schq); else if (parent->level == NIX_TXSCH_LVL_TL3) cfg->reg[0] = NIX_AF_TL3X_TOPOLOGY(parent->schq); else if (parent->level == NIX_TXSCH_LVL_TL2) cfg->reg[0] = NIX_AF_TL2X_TOPOLOGY(parent->schq); else if (parent->level == NIX_TXSCH_LVL_TL1) cfg->reg[0] = NIX_AF_TL1X_TOPOLOGY(parent->schq); cfg->regval[0] = (u64)parent->prio_anchor << 32; cfg->regval[0] |= ((parent->child_dwrr_prio != OTX2_QOS_DEFAULT_PRIO) ? parent->child_dwrr_prio : 0) << 1; cfg->num_regs++; rc = otx2_sync_mbox_msg(&pfvf->mbox); mutex_unlock(&mbox->lock); return rc; } static void otx2_qos_free_hw_node_schq(struct otx2_nic *pfvf, struct otx2_qos_node *parent) { struct otx2_qos_node *node; list_for_each_entry_reverse(node, &parent->child_schq_list, list) otx2_txschq_free_one(pfvf, node->level, node->schq); } static void otx2_qos_free_hw_node(struct otx2_nic *pfvf, struct otx2_qos_node *parent) { struct otx2_qos_node *node, *tmp; list_for_each_entry_safe(node, tmp, &parent->child_list, list) { otx2_qos_free_hw_node(pfvf, node); otx2_qos_free_hw_node_schq(pfvf, node); otx2_txschq_free_one(pfvf, node->level, node->schq); } } static void otx2_qos_free_hw_cfg(struct otx2_nic *pfvf, struct otx2_qos_node *node) { mutex_lock(&pfvf->qos.qos_lock); /* free child node hw mappings */ otx2_qos_free_hw_node(pfvf, node); otx2_qos_free_hw_node_schq(pfvf, node); /* free node hw mappings */ otx2_txschq_free_one(pfvf, node->level, node->schq); mutex_unlock(&pfvf->qos.qos_lock); } static void otx2_qos_sw_node_delete(struct otx2_nic *pfvf, struct otx2_qos_node *node) { hash_del_rcu(&node->hlist); if (node->qid != OTX2_QOS_QID_INNER && node->qid != OTX2_QOS_QID_NONE) { __clear_bit(node->qid, pfvf->qos.qos_sq_bmap); otx2_qos_update_tx_netdev_queues(pfvf); } list_del(&node->list); kfree(node); } static void otx2_qos_free_sw_node_schq(struct otx2_nic *pfvf, struct otx2_qos_node *parent) { struct otx2_qos_node *node, *tmp; list_for_each_entry_safe(node, tmp, &parent->child_schq_list, list) { list_del(&node->list); kfree(node); } } static void __otx2_qos_free_sw_node(struct otx2_nic *pfvf, struct otx2_qos_node *parent) { struct otx2_qos_node *node, *tmp; list_for_each_entry_safe(node, tmp, &parent->child_list, list) { __otx2_qos_free_sw_node(pfvf, node); otx2_qos_free_sw_node_schq(pfvf, node); otx2_qos_sw_node_delete(pfvf, node); } } static void otx2_qos_free_sw_node(struct otx2_nic *pfvf, struct otx2_qos_node *node) { mutex_lock(&pfvf->qos.qos_lock); __otx2_qos_free_sw_node(pfvf, node); otx2_qos_free_sw_node_schq(pfvf, node); otx2_qos_sw_node_delete(pfvf, node); mutex_unlock(&pfvf->qos.qos_lock); } static void otx2_qos_destroy_node(struct otx2_nic *pfvf, struct otx2_qos_node *node) { otx2_qos_free_hw_cfg(pfvf, node); otx2_qos_free_sw_node(pfvf, node); } static void otx2_qos_fill_cfg_schq(struct otx2_qos_node *parent, struct otx2_qos_cfg *cfg) { struct otx2_qos_node *node; list_for_each_entry(node, &parent->child_schq_list, list) cfg->schq[node->level]++; } static void otx2_qos_fill_cfg_tl(struct otx2_qos_node *parent, struct otx2_qos_cfg *cfg) { struct otx2_qos_node *node; list_for_each_entry(node, &parent->child_list, list) { otx2_qos_fill_cfg_tl(node, cfg); otx2_qos_fill_cfg_schq(node, cfg); } /* Assign the required number of transmit schedular queues under the * given class */ cfg->schq_contig[parent->level - 1] += parent->child_dwrr_cnt + parent->max_static_prio + 1; } static void otx2_qos_prepare_txschq_cfg(struct otx2_nic *pfvf, struct otx2_qos_node *parent, struct otx2_qos_cfg *cfg) { mutex_lock(&pfvf->qos.qos_lock); otx2_qos_fill_cfg_tl(parent, cfg); mutex_unlock(&pfvf->qos.qos_lock); } static void otx2_qos_read_txschq_cfg_schq(struct otx2_qos_node *parent, struct otx2_qos_cfg *cfg) { struct otx2_qos_node *node; int cnt; list_for_each_entry(node, &parent->child_schq_list, list) { cnt = cfg->dwrr_node_pos[node->level]; cfg->schq_list[node->level][cnt] = node->schq; cfg->schq[node->level]++; cfg->dwrr_node_pos[node->level]++; } } static void otx2_qos_read_txschq_cfg_tl(struct otx2_qos_node *parent, struct otx2_qos_cfg *cfg) { struct otx2_qos_node *node; int cnt; list_for_each_entry(node, &parent->child_list, list) { otx2_qos_read_txschq_cfg_tl(node, cfg); cnt = cfg->static_node_pos[node->level]; cfg->schq_contig_list[node->level][cnt] = node->schq; cfg->schq_index_used[node->level][cnt] = true; cfg->schq_contig[node->level]++; cfg->static_node_pos[node->level]++; otx2_qos_read_txschq_cfg_schq(node, cfg); } } static void otx2_qos_read_txschq_cfg(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct otx2_qos_cfg *cfg) { mutex_lock(&pfvf->qos.qos_lock); otx2_qos_read_txschq_cfg_tl(node, cfg); mutex_unlock(&pfvf->qos.qos_lock); } static struct otx2_qos_node * otx2_qos_alloc_root(struct otx2_nic *pfvf) { struct otx2_qos_node *node; node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) return ERR_PTR(-ENOMEM); node->parent = NULL; if (!is_otx2_vf(pfvf->pcifunc)) { node->level = NIX_TXSCH_LVL_TL1; } else { node->level = NIX_TXSCH_LVL_TL2; node->child_dwrr_prio = OTX2_QOS_DEFAULT_PRIO; } WRITE_ONCE(node->qid, OTX2_QOS_QID_INNER); node->classid = OTX2_QOS_ROOT_CLASSID; hash_add_rcu(pfvf->qos.qos_hlist, &node->hlist, node->classid); list_add_tail(&node->list, &pfvf->qos.qos_tree); INIT_LIST_HEAD(&node->child_list); INIT_LIST_HEAD(&node->child_schq_list); return node; } static int otx2_qos_add_child_node(struct otx2_qos_node *parent, struct otx2_qos_node *node) { struct list_head *head = &parent->child_list; struct otx2_qos_node *tmp_node; struct list_head *tmp; if (node->prio > parent->max_static_prio) parent->max_static_prio = node->prio; for (tmp = head->next; tmp != head; tmp = tmp->next) { tmp_node = list_entry(tmp, struct otx2_qos_node, list); if (tmp_node->prio == node->prio && tmp_node->is_static) return -EEXIST; if (tmp_node->prio > node->prio) { list_add_tail(&node->list, tmp); return 0; } } list_add_tail(&node->list, head); return 0; } static int otx2_qos_alloc_txschq_node(struct otx2_nic *pfvf, struct otx2_qos_node *node) { struct otx2_qos_node *txschq_node, *parent, *tmp; int lvl; parent = node; for (lvl = node->level - 1; lvl >= NIX_TXSCH_LVL_MDQ; lvl--) { txschq_node = kzalloc(sizeof(*txschq_node), GFP_KERNEL); if (!txschq_node) goto err_out; txschq_node->parent = parent; txschq_node->level = lvl; txschq_node->classid = OTX2_QOS_CLASS_NONE; WRITE_ONCE(txschq_node->qid, OTX2_QOS_QID_NONE); txschq_node->rate = 0; txschq_node->ceil = 0; txschq_node->prio = 0; txschq_node->quantum = 0; txschq_node->is_static = true; txschq_node->child_dwrr_prio = OTX2_QOS_DEFAULT_PRIO; txschq_node->txschq_idx = OTX2_QOS_INVALID_TXSCHQ_IDX; mutex_lock(&pfvf->qos.qos_lock); list_add_tail(&txschq_node->list, &node->child_schq_list); mutex_unlock(&pfvf->qos.qos_lock); INIT_LIST_HEAD(&txschq_node->child_list); INIT_LIST_HEAD(&txschq_node->child_schq_list); parent = txschq_node; } return 0; err_out: list_for_each_entry_safe(txschq_node, tmp, &node->child_schq_list, list) { list_del(&txschq_node->list); kfree(txschq_node); } return -ENOMEM; } static struct otx2_qos_node * otx2_qos_sw_create_leaf_node(struct otx2_nic *pfvf, struct otx2_qos_node *parent, u16 classid, u32 prio, u64 rate, u64 ceil, u32 quantum, u16 qid, bool static_cfg) { struct otx2_qos_node *node; int err; node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) return ERR_PTR(-ENOMEM); node->parent = parent; node->level = parent->level - 1; node->classid = classid; WRITE_ONCE(node->qid, qid); node->rate = otx2_convert_rate(rate); node->ceil = otx2_convert_rate(ceil); node->prio = prio; node->quantum = quantum; node->is_static = static_cfg; node->child_dwrr_prio = OTX2_QOS_DEFAULT_PRIO; node->txschq_idx = OTX2_QOS_INVALID_TXSCHQ_IDX; __set_bit(qid, pfvf->qos.qos_sq_bmap); hash_add_rcu(pfvf->qos.qos_hlist, &node->hlist, classid); mutex_lock(&pfvf->qos.qos_lock); err = otx2_qos_add_child_node(parent, node); if (err) { mutex_unlock(&pfvf->qos.qos_lock); return ERR_PTR(err); } mutex_unlock(&pfvf->qos.qos_lock); INIT_LIST_HEAD(&node->child_list); INIT_LIST_HEAD(&node->child_schq_list); err = otx2_qos_alloc_txschq_node(pfvf, node); if (err) { otx2_qos_sw_node_delete(pfvf, node); return ERR_PTR(-ENOMEM); } return node; } static struct otx2_qos_node * otx2_sw_node_find(struct otx2_nic *pfvf, u32 classid) { struct otx2_qos_node *node = NULL; hash_for_each_possible(pfvf->qos.qos_hlist, node, hlist, classid) { if (node->classid == classid) break; } return node; } static struct otx2_qos_node * otx2_sw_node_find_rcu(struct otx2_nic *pfvf, u32 classid) { struct otx2_qos_node *node = NULL; hash_for_each_possible_rcu(pfvf->qos.qos_hlist, node, hlist, classid) { if (node->classid == classid) break; } return node; } int otx2_get_txq_by_classid(struct otx2_nic *pfvf, u16 classid) { struct otx2_qos_node *node; u16 qid; int res; node = otx2_sw_node_find_rcu(pfvf, classid); if (!node) { res = -ENOENT; goto out; } qid = READ_ONCE(node->qid); if (qid == OTX2_QOS_QID_INNER) { res = -EINVAL; goto out; } res = pfvf->hw.tx_queues + qid; out: return res; } static int otx2_qos_txschq_config(struct otx2_nic *pfvf, struct otx2_qos_node *node) { struct mbox *mbox = &pfvf->mbox; struct nix_txschq_config *req; int rc; mutex_lock(&mbox->lock); req = otx2_mbox_alloc_msg_nix_txschq_cfg(&pfvf->mbox); if (!req) { mutex_unlock(&mbox->lock); return -ENOMEM; } req->lvl = node->level; __otx2_qos_txschq_cfg(pfvf, node, req); rc = otx2_sync_mbox_msg(&pfvf->mbox); mutex_unlock(&mbox->lock); return rc; } static int otx2_qos_txschq_alloc(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg) { struct nix_txsch_alloc_req *req; struct nix_txsch_alloc_rsp *rsp; struct mbox *mbox = &pfvf->mbox; int lvl, rc, schq; mutex_lock(&mbox->lock); req = otx2_mbox_alloc_msg_nix_txsch_alloc(&pfvf->mbox); if (!req) { mutex_unlock(&mbox->lock); return -ENOMEM; } for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { req->schq[lvl] = cfg->schq[lvl]; req->schq_contig[lvl] = cfg->schq_contig[lvl]; } rc = otx2_sync_mbox_msg(&pfvf->mbox); if (rc) { mutex_unlock(&mbox->lock); return rc; } rsp = (struct nix_txsch_alloc_rsp *) otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr); if (IS_ERR(rsp)) { rc = PTR_ERR(rsp); goto out; } for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { for (schq = 0; schq < rsp->schq_contig[lvl]; schq++) { cfg->schq_contig_list[lvl][schq] = rsp->schq_contig_list[lvl][schq]; } } for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { for (schq = 0; schq < rsp->schq[lvl]; schq++) { cfg->schq_list[lvl][schq] = rsp->schq_list[lvl][schq]; } } pfvf->qos.link_cfg_lvl = rsp->link_cfg_lvl; pfvf->hw.txschq_aggr_lvl_rr_prio = rsp->aggr_lvl_rr_prio; out: mutex_unlock(&mbox->lock); return rc; } static void otx2_qos_free_unused_txschq(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg) { int lvl, idx, schq; for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { for (idx = 0; idx < cfg->schq_contig[lvl]; idx++) { if (!cfg->schq_index_used[lvl][idx]) { schq = cfg->schq_contig_list[lvl][idx]; otx2_txschq_free_one(pfvf, lvl, schq); } } } } static void otx2_qos_txschq_fill_cfg_schq(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct otx2_qos_cfg *cfg) { struct otx2_qos_node *tmp; int cnt; list_for_each_entry(tmp, &node->child_schq_list, list) { cnt = cfg->dwrr_node_pos[tmp->level]; tmp->schq = cfg->schq_list[tmp->level][cnt]; cfg->dwrr_node_pos[tmp->level]++; } } static void otx2_qos_txschq_fill_cfg_tl(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct otx2_qos_cfg *cfg) { struct otx2_qos_node *tmp; int cnt; list_for_each_entry(tmp, &node->child_list, list) { otx2_qos_txschq_fill_cfg_tl(pfvf, tmp, cfg); cnt = cfg->static_node_pos[tmp->level]; tmp->schq = cfg->schq_contig_list[tmp->level][tmp->txschq_idx]; cfg->schq_index_used[tmp->level][tmp->txschq_idx] = true; if (cnt == 0) node->prio_anchor = cfg->schq_contig_list[tmp->level][0]; cfg->static_node_pos[tmp->level]++; otx2_qos_txschq_fill_cfg_schq(pfvf, tmp, cfg); } } static void otx2_qos_txschq_fill_cfg(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct otx2_qos_cfg *cfg) { mutex_lock(&pfvf->qos.qos_lock); otx2_qos_txschq_fill_cfg_tl(pfvf, node, cfg); otx2_qos_txschq_fill_cfg_schq(pfvf, node, cfg); otx2_qos_free_unused_txschq(pfvf, cfg); mutex_unlock(&pfvf->qos.qos_lock); } static void __otx2_qos_assign_base_idx_tl(struct otx2_nic *pfvf, struct otx2_qos_node *tmp, unsigned long *child_idx_bmap, int child_cnt) { int idx; if (tmp->txschq_idx != OTX2_QOS_INVALID_TXSCHQ_IDX) return; /* assign static nodes 1:1 prio mapping first, then remaining nodes */ for (idx = 0; idx < child_cnt; idx++) { if (tmp->is_static && tmp->prio == idx && !test_bit(idx, child_idx_bmap)) { tmp->txschq_idx = idx; set_bit(idx, child_idx_bmap); return; } else if (!tmp->is_static && idx >= tmp->prio && !test_bit(idx, child_idx_bmap)) { tmp->txschq_idx = idx; set_bit(idx, child_idx_bmap); return; } } } static int otx2_qos_assign_base_idx_tl(struct otx2_nic *pfvf, struct otx2_qos_node *node) { unsigned long *child_idx_bmap; struct otx2_qos_node *tmp; int child_cnt; list_for_each_entry(tmp, &node->child_list, list) tmp->txschq_idx = OTX2_QOS_INVALID_TXSCHQ_IDX; /* allocate child index array */ child_cnt = node->child_dwrr_cnt + node->max_static_prio + 1; child_idx_bmap = kcalloc(BITS_TO_LONGS(child_cnt), sizeof(unsigned long), GFP_KERNEL); if (!child_idx_bmap) return -ENOMEM; list_for_each_entry(tmp, &node->child_list, list) otx2_qos_assign_base_idx_tl(pfvf, tmp); /* assign base index of static priority children first */ list_for_each_entry(tmp, &node->child_list, list) { if (!tmp->is_static) continue; __otx2_qos_assign_base_idx_tl(pfvf, tmp, child_idx_bmap, child_cnt); } /* assign base index of dwrr priority children */ list_for_each_entry(tmp, &node->child_list, list) __otx2_qos_assign_base_idx_tl(pfvf, tmp, child_idx_bmap, child_cnt); kfree(child_idx_bmap); return 0; } static int otx2_qos_assign_base_idx(struct otx2_nic *pfvf, struct otx2_qos_node *node) { int ret = 0; mutex_lock(&pfvf->qos.qos_lock); ret = otx2_qos_assign_base_idx_tl(pfvf, node); mutex_unlock(&pfvf->qos.qos_lock); return ret; } static int otx2_qos_txschq_push_cfg_schq(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct otx2_qos_cfg *cfg) { struct otx2_qos_node *tmp; int ret; list_for_each_entry(tmp, &node->child_schq_list, list) { ret = otx2_qos_txschq_config(pfvf, tmp); if (ret) return -EIO; ret = otx2_qos_txschq_set_parent_topology(pfvf, tmp->parent); if (ret) return -EIO; } return 0; } static int otx2_qos_txschq_push_cfg_tl(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct otx2_qos_cfg *cfg) { struct otx2_qos_node *tmp; int ret; list_for_each_entry(tmp, &node->child_list, list) { ret = otx2_qos_txschq_push_cfg_tl(pfvf, tmp, cfg); if (ret) return -EIO; ret = otx2_qos_txschq_config(pfvf, tmp); if (ret) return -EIO; ret = otx2_qos_txschq_push_cfg_schq(pfvf, tmp, cfg); if (ret) return -EIO; } ret = otx2_qos_txschq_set_parent_topology(pfvf, node); if (ret) return -EIO; return 0; } static int otx2_qos_txschq_push_cfg(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct otx2_qos_cfg *cfg) { int ret; mutex_lock(&pfvf->qos.qos_lock); ret = otx2_qos_txschq_push_cfg_tl(pfvf, node, cfg); if (ret) goto out; ret = otx2_qos_txschq_push_cfg_schq(pfvf, node, cfg); out: mutex_unlock(&pfvf->qos.qos_lock); return ret; } static int otx2_qos_txschq_update_config(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct otx2_qos_cfg *cfg) { otx2_qos_txschq_fill_cfg(pfvf, node, cfg); return otx2_qos_txschq_push_cfg(pfvf, node, cfg); } static int otx2_qos_txschq_update_root_cfg(struct otx2_nic *pfvf, struct otx2_qos_node *root, struct otx2_qos_cfg *cfg) { root->schq = cfg->schq_list[root->level][0]; return otx2_qos_txschq_config(pfvf, root); } static void otx2_qos_free_cfg(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg) { int lvl, idx, schq; for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { for (idx = 0; idx < cfg->schq[lvl]; idx++) { schq = cfg->schq_list[lvl][idx]; otx2_txschq_free_one(pfvf, lvl, schq); } } for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { for (idx = 0; idx < cfg->schq_contig[lvl]; idx++) { if (cfg->schq_index_used[lvl][idx]) { schq = cfg->schq_contig_list[lvl][idx]; otx2_txschq_free_one(pfvf, lvl, schq); } } } } static void otx2_qos_enadis_sq(struct otx2_nic *pfvf, struct otx2_qos_node *node, u16 qid) { if (pfvf->qos.qid_to_sqmap[qid] != OTX2_QOS_INVALID_SQ) otx2_qos_disable_sq(pfvf, qid); pfvf->qos.qid_to_sqmap[qid] = node->schq; otx2_qos_enable_sq(pfvf, qid); } static void otx2_qos_update_smq_schq(struct otx2_nic *pfvf, struct otx2_qos_node *node, bool action) { struct otx2_qos_node *tmp; if (node->qid == OTX2_QOS_QID_INNER) return; list_for_each_entry(tmp, &node->child_schq_list, list) { if (tmp->level == NIX_TXSCH_LVL_MDQ) { if (action == QOS_SMQ_FLUSH) otx2_smq_flush(pfvf, tmp->schq); else otx2_qos_enadis_sq(pfvf, tmp, node->qid); } } } static void __otx2_qos_update_smq(struct otx2_nic *pfvf, struct otx2_qos_node *node, bool action) { struct otx2_qos_node *tmp; list_for_each_entry(tmp, &node->child_list, list) { __otx2_qos_update_smq(pfvf, tmp, action); if (tmp->qid == OTX2_QOS_QID_INNER) continue; if (tmp->level == NIX_TXSCH_LVL_MDQ) { if (action == QOS_SMQ_FLUSH) otx2_smq_flush(pfvf, tmp->schq); else otx2_qos_enadis_sq(pfvf, tmp, tmp->qid); } else { otx2_qos_update_smq_schq(pfvf, tmp, action); } } } static void otx2_qos_update_smq(struct otx2_nic *pfvf, struct otx2_qos_node *node, bool action) { mutex_lock(&pfvf->qos.qos_lock); __otx2_qos_update_smq(pfvf, node, action); otx2_qos_update_smq_schq(pfvf, node, action); mutex_unlock(&pfvf->qos.qos_lock); } static int otx2_qos_push_txschq_cfg(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct otx2_qos_cfg *cfg) { int ret; ret = otx2_qos_txschq_alloc(pfvf, cfg); if (ret) return -ENOSPC; ret = otx2_qos_assign_base_idx(pfvf, node); if (ret) return -ENOMEM; if (!(pfvf->netdev->flags & IFF_UP)) { otx2_qos_txschq_fill_cfg(pfvf, node, cfg); return 0; } ret = otx2_qos_txschq_update_config(pfvf, node, cfg); if (ret) { otx2_qos_free_cfg(pfvf, cfg); return -EIO; } otx2_qos_update_smq(pfvf, node, QOS_CFG_SQ); return 0; } static int otx2_qos_update_tree(struct otx2_nic *pfvf, struct otx2_qos_node *node, struct otx2_qos_cfg *cfg) { otx2_qos_prepare_txschq_cfg(pfvf, node->parent, cfg); return otx2_qos_push_txschq_cfg(pfvf, node->parent, cfg); } static int otx2_qos_root_add(struct otx2_nic *pfvf, u16 htb_maj_id, u16 htb_defcls, struct netlink_ext_ack *extack) { struct otx2_qos_cfg *new_cfg; struct otx2_qos_node *root; int err; netdev_dbg(pfvf->netdev, "TC_HTB_CREATE: handle=0x%x defcls=0x%x\n", htb_maj_id, htb_defcls); root = otx2_qos_alloc_root(pfvf); if (IS_ERR(root)) { err = PTR_ERR(root); return err; } /* allocate txschq queue */ new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); if (!new_cfg) { NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); err = -ENOMEM; goto free_root_node; } /* allocate htb root node */ new_cfg->schq[root->level] = 1; err = otx2_qos_txschq_alloc(pfvf, new_cfg); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error allocating txschq"); goto free_root_node; } /* Update TL1 RR PRIO */ if (root->level == NIX_TXSCH_LVL_TL1) { root->child_dwrr_prio = pfvf->hw.txschq_aggr_lvl_rr_prio; netdev_dbg(pfvf->netdev, "TL1 DWRR Priority %d\n", root->child_dwrr_prio); } if (!(pfvf->netdev->flags & IFF_UP) || root->level == NIX_TXSCH_LVL_TL1) { root->schq = new_cfg->schq_list[root->level][0]; goto out; } /* update the txschq configuration in hw */ err = otx2_qos_txschq_update_root_cfg(pfvf, root, new_cfg); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error updating txschq configuration"); goto txschq_free; } out: WRITE_ONCE(pfvf->qos.defcls, htb_defcls); /* Pairs with smp_load_acquire() in ndo_select_queue */ smp_store_release(&pfvf->qos.maj_id, htb_maj_id); kfree(new_cfg); return 0; txschq_free: otx2_qos_free_cfg(pfvf, new_cfg); free_root_node: kfree(new_cfg); otx2_qos_sw_node_delete(pfvf, root); return err; } static int otx2_qos_root_destroy(struct otx2_nic *pfvf) { struct otx2_qos_node *root; netdev_dbg(pfvf->netdev, "TC_HTB_DESTROY\n"); /* find root node */ root = otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID); if (!root) return -ENOENT; /* free the hw mappings */ otx2_qos_destroy_node(pfvf, root); return 0; } static int otx2_qos_validate_quantum(struct otx2_nic *pfvf, u32 quantum) { u32 rr_weight = otx2_qos_quantum_to_dwrr_weight(pfvf, quantum); int err = 0; /* Max Round robin weight supported by octeontx2 and CN10K * is different. Validate accordingly */ if (is_dev_otx2(pfvf->pdev)) err = (rr_weight > OTX2_MAX_RR_QUANTUM) ? -EINVAL : 0; else if (rr_weight > CN10K_MAX_RR_WEIGHT) err = -EINVAL; return err; } static int otx2_qos_validate_dwrr_cfg(struct otx2_qos_node *parent, struct netlink_ext_ack *extack, struct otx2_nic *pfvf, u64 prio, u64 quantum) { int err; err = otx2_qos_validate_quantum(pfvf, quantum); if (err) { NL_SET_ERR_MSG_MOD(extack, "Unsupported quantum value"); return err; } if (parent->child_dwrr_prio == OTX2_QOS_DEFAULT_PRIO) { parent->child_dwrr_prio = prio; } else if (prio != parent->child_dwrr_prio) { NL_SET_ERR_MSG_MOD(extack, "Only one DWRR group is allowed"); return -EOPNOTSUPP; } return 0; } static int otx2_qos_validate_configuration(struct otx2_qos_node *parent, struct netlink_ext_ack *extack, struct otx2_nic *pfvf, u64 prio, bool static_cfg) { if (prio == parent->child_dwrr_prio && static_cfg) { NL_SET_ERR_MSG_MOD(extack, "DWRR child group with same priority exists"); return -EEXIST; } if (static_cfg && test_bit(prio, parent->prio_bmap)) { NL_SET_ERR_MSG_MOD(extack, "Static priority child with same priority exists"); return -EEXIST; } return 0; } static void otx2_reset_dwrr_prio(struct otx2_qos_node *parent, u64 prio) { /* For PF, root node dwrr priority is static */ if (parent->level == NIX_TXSCH_LVL_TL1) return; if (parent->child_dwrr_prio != OTX2_QOS_DEFAULT_PRIO) { parent->child_dwrr_prio = OTX2_QOS_DEFAULT_PRIO; clear_bit(prio, parent->prio_bmap); } } static bool is_qos_node_dwrr(struct otx2_qos_node *parent, struct otx2_nic *pfvf, u64 prio) { struct otx2_qos_node *node; bool ret = false; if (parent->child_dwrr_prio == prio) return true; mutex_lock(&pfvf->qos.qos_lock); list_for_each_entry(node, &parent->child_list, list) { if (prio == node->prio) { if (parent->child_dwrr_prio != OTX2_QOS_DEFAULT_PRIO && parent->child_dwrr_prio != prio) continue; if (otx2_qos_validate_quantum(pfvf, node->quantum)) { netdev_err(pfvf->netdev, "Unsupported quantum value for existing classid=0x%x quantum=%d prio=%d", node->classid, node->quantum, node->prio); break; } /* mark old node as dwrr */ node->is_static = false; parent->child_dwrr_cnt++; parent->child_static_cnt--; ret = true; break; } } mutex_unlock(&pfvf->qos.qos_lock); return ret; } static int otx2_qos_leaf_alloc_queue(struct otx2_nic *pfvf, u16 classid, u32 parent_classid, u64 rate, u64 ceil, u64 prio, u32 quantum, struct netlink_ext_ack *extack) { struct otx2_qos_cfg *old_cfg, *new_cfg; struct otx2_qos_node *node, *parent; int qid, ret, err; bool static_cfg; netdev_dbg(pfvf->netdev, "TC_HTB_LEAF_ALLOC_QUEUE: classid=0x%x parent_classid=0x%x rate=%lld ceil=%lld prio=%lld quantum=%d\n", classid, parent_classid, rate, ceil, prio, quantum); if (prio > OTX2_QOS_MAX_PRIO) { NL_SET_ERR_MSG_MOD(extack, "Valid priority range 0 to 7"); ret = -EOPNOTSUPP; goto out; } if (!quantum || quantum > INT_MAX) { NL_SET_ERR_MSG_MOD(extack, "Invalid quantum, range 1 - 2147483647 bytes"); ret = -EOPNOTSUPP; goto out; } /* get parent node */ parent = otx2_sw_node_find(pfvf, parent_classid); if (!parent) { NL_SET_ERR_MSG_MOD(extack, "parent node not found"); ret = -ENOENT; goto out; } if (parent->level == NIX_TXSCH_LVL_MDQ) { NL_SET_ERR_MSG_MOD(extack, "HTB qos max levels reached"); ret = -EOPNOTSUPP; goto out; } static_cfg = !is_qos_node_dwrr(parent, pfvf, prio); ret = otx2_qos_validate_configuration(parent, extack, pfvf, prio, static_cfg); if (ret) goto out; if (!static_cfg) { ret = otx2_qos_validate_dwrr_cfg(parent, extack, pfvf, prio, quantum); if (ret) goto out; } if (static_cfg) parent->child_static_cnt++; else parent->child_dwrr_cnt++; set_bit(prio, parent->prio_bmap); /* read current txschq configuration */ old_cfg = kzalloc(sizeof(*old_cfg), GFP_KERNEL); if (!old_cfg) { NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); ret = -ENOMEM; goto reset_prio; } otx2_qos_read_txschq_cfg(pfvf, parent, old_cfg); /* allocate a new sq */ qid = otx2_qos_get_qid(pfvf); if (qid < 0) { NL_SET_ERR_MSG_MOD(extack, "Reached max supported QOS SQ's"); ret = -ENOMEM; goto free_old_cfg; } /* Actual SQ mapping will be updated after SMQ alloc */ pfvf->qos.qid_to_sqmap[qid] = OTX2_QOS_INVALID_SQ; /* allocate and initialize a new child node */ node = otx2_qos_sw_create_leaf_node(pfvf, parent, classid, prio, rate, ceil, quantum, qid, static_cfg); if (IS_ERR(node)) { NL_SET_ERR_MSG_MOD(extack, "Unable to allocate leaf node"); ret = PTR_ERR(node); goto free_old_cfg; } /* push new txschq config to hw */ new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); if (!new_cfg) { NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); ret = -ENOMEM; goto free_node; } ret = otx2_qos_update_tree(pfvf, node, new_cfg); if (ret) { NL_SET_ERR_MSG_MOD(extack, "HTB HW configuration error"); kfree(new_cfg); otx2_qos_sw_node_delete(pfvf, node); /* restore the old qos tree */ err = otx2_qos_txschq_update_config(pfvf, parent, old_cfg); if (err) { netdev_err(pfvf->netdev, "Failed to restore txcshq configuration"); goto free_old_cfg; } otx2_qos_update_smq(pfvf, parent, QOS_CFG_SQ); goto free_old_cfg; } /* update tx_real_queues */ otx2_qos_update_tx_netdev_queues(pfvf); /* free new txschq config */ kfree(new_cfg); /* free old txschq config */ otx2_qos_free_cfg(pfvf, old_cfg); kfree(old_cfg); return pfvf->hw.tx_queues + qid; free_node: otx2_qos_sw_node_delete(pfvf, node); free_old_cfg: kfree(old_cfg); reset_prio: if (static_cfg) parent->child_static_cnt--; else parent->child_dwrr_cnt--; clear_bit(prio, parent->prio_bmap); out: return ret; } static int otx2_qos_leaf_to_inner(struct otx2_nic *pfvf, u16 classid, u16 child_classid, u64 rate, u64 ceil, u64 prio, u32 quantum, struct netlink_ext_ack *extack) { struct otx2_qos_cfg *old_cfg, *new_cfg; struct otx2_qos_node *node, *child; bool static_cfg; int ret, err; u16 qid; netdev_dbg(pfvf->netdev, "TC_HTB_LEAF_TO_INNER classid %04x, child %04x, rate %llu, ceil %llu\n", classid, child_classid, rate, ceil); if (prio > OTX2_QOS_MAX_PRIO) { NL_SET_ERR_MSG_MOD(extack, "Valid priority range 0 to 7"); ret = -EOPNOTSUPP; goto out; } if (!quantum || quantum > INT_MAX) { NL_SET_ERR_MSG_MOD(extack, "Invalid quantum, range 1 - 2147483647 bytes"); ret = -EOPNOTSUPP; goto out; } /* find node related to classid */ node = otx2_sw_node_find(pfvf, classid); if (!node) { NL_SET_ERR_MSG_MOD(extack, "HTB node not found"); ret = -ENOENT; goto out; } /* check max qos txschq level */ if (node->level == NIX_TXSCH_LVL_MDQ) { NL_SET_ERR_MSG_MOD(extack, "HTB qos level not supported"); ret = -EOPNOTSUPP; goto out; } static_cfg = !is_qos_node_dwrr(node, pfvf, prio); if (!static_cfg) { ret = otx2_qos_validate_dwrr_cfg(node, extack, pfvf, prio, quantum); if (ret) goto out; } if (static_cfg) node->child_static_cnt++; else node->child_dwrr_cnt++; set_bit(prio, node->prio_bmap); /* store the qid to assign to leaf node */ qid = node->qid; /* read current txschq configuration */ old_cfg = kzalloc(sizeof(*old_cfg), GFP_KERNEL); if (!old_cfg) { NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); ret = -ENOMEM; goto reset_prio; } otx2_qos_read_txschq_cfg(pfvf, node, old_cfg); /* delete the txschq nodes allocated for this node */ otx2_qos_free_sw_node_schq(pfvf, node); /* mark this node as htb inner node */ WRITE_ONCE(node->qid, OTX2_QOS_QID_INNER); /* allocate and initialize a new child node */ child = otx2_qos_sw_create_leaf_node(pfvf, node, child_classid, prio, rate, ceil, quantum, qid, static_cfg); if (IS_ERR(child)) { NL_SET_ERR_MSG_MOD(extack, "Unable to allocate leaf node"); ret = PTR_ERR(child); goto free_old_cfg; } /* push new txschq config to hw */ new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); if (!new_cfg) { NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); ret = -ENOMEM; goto free_node; } ret = otx2_qos_update_tree(pfvf, child, new_cfg); if (ret) { NL_SET_ERR_MSG_MOD(extack, "HTB HW configuration error"); kfree(new_cfg); otx2_qos_sw_node_delete(pfvf, child); /* restore the old qos tree */ WRITE_ONCE(node->qid, qid); err = otx2_qos_alloc_txschq_node(pfvf, node); if (err) { netdev_err(pfvf->netdev, "Failed to restore old leaf node"); goto free_old_cfg; } err = otx2_qos_txschq_update_config(pfvf, node, old_cfg); if (err) { netdev_err(pfvf->netdev, "Failed to restore txcshq configuration"); goto free_old_cfg; } otx2_qos_update_smq(pfvf, node, QOS_CFG_SQ); goto free_old_cfg; } /* free new txschq config */ kfree(new_cfg); /* free old txschq config */ otx2_qos_free_cfg(pfvf, old_cfg); kfree(old_cfg); return 0; free_node: otx2_qos_sw_node_delete(pfvf, child); free_old_cfg: kfree(old_cfg); reset_prio: if (static_cfg) node->child_static_cnt--; else node->child_dwrr_cnt--; clear_bit(prio, node->prio_bmap); out: return ret; } static int otx2_qos_leaf_del(struct otx2_nic *pfvf, u16 *classid, struct netlink_ext_ack *extack) { struct otx2_qos_node *node, *parent; int dwrr_del_node = false; u64 prio; u16 qid; netdev_dbg(pfvf->netdev, "TC_HTB_LEAF_DEL classid %04x\n", *classid); /* find node related to classid */ node = otx2_sw_node_find(pfvf, *classid); if (!node) { NL_SET_ERR_MSG_MOD(extack, "HTB node not found"); return -ENOENT; } parent = node->parent; prio = node->prio; qid = node->qid; if (!node->is_static) dwrr_del_node = true; otx2_qos_disable_sq(pfvf, node->qid); otx2_qos_destroy_node(pfvf, node); pfvf->qos.qid_to_sqmap[qid] = OTX2_QOS_INVALID_SQ; if (dwrr_del_node) { parent->child_dwrr_cnt--; } else { parent->child_static_cnt--; clear_bit(prio, parent->prio_bmap); } /* Reset DWRR priority if all dwrr nodes are deleted */ if (!parent->child_dwrr_cnt) otx2_reset_dwrr_prio(parent, prio); if (!parent->child_static_cnt) parent->max_static_prio = 0; return 0; } static int otx2_qos_leaf_del_last(struct otx2_nic *pfvf, u16 classid, bool force, struct netlink_ext_ack *extack) { struct otx2_qos_node *node, *parent; struct otx2_qos_cfg *new_cfg; int dwrr_del_node = false; u64 prio; int err; u16 qid; netdev_dbg(pfvf->netdev, "TC_HTB_LEAF_DEL_LAST classid %04x\n", classid); /* find node related to classid */ node = otx2_sw_node_find(pfvf, classid); if (!node) { NL_SET_ERR_MSG_MOD(extack, "HTB node not found"); return -ENOENT; } /* save qid for use by parent */ qid = node->qid; prio = node->prio; parent = otx2_sw_node_find(pfvf, node->parent->classid); if (!parent) { NL_SET_ERR_MSG_MOD(extack, "parent node not found"); return -ENOENT; } if (!node->is_static) dwrr_del_node = true; /* destroy the leaf node */ otx2_qos_destroy_node(pfvf, node); pfvf->qos.qid_to_sqmap[qid] = OTX2_QOS_INVALID_SQ; if (dwrr_del_node) { parent->child_dwrr_cnt--; } else { parent->child_static_cnt--; clear_bit(prio, parent->prio_bmap); } /* Reset DWRR priority if all dwrr nodes are deleted */ if (!parent->child_dwrr_cnt) otx2_reset_dwrr_prio(parent, prio); if (!parent->child_static_cnt) parent->max_static_prio = 0; /* create downstream txschq entries to parent */ err = otx2_qos_alloc_txschq_node(pfvf, parent); if (err) { NL_SET_ERR_MSG_MOD(extack, "HTB failed to create txsch configuration"); return err; } WRITE_ONCE(parent->qid, qid); __set_bit(qid, pfvf->qos.qos_sq_bmap); /* push new txschq config to hw */ new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); if (!new_cfg) { NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); return -ENOMEM; } /* fill txschq cfg and push txschq cfg to hw */ otx2_qos_fill_cfg_schq(parent, new_cfg); err = otx2_qos_push_txschq_cfg(pfvf, parent, new_cfg); if (err) { NL_SET_ERR_MSG_MOD(extack, "HTB HW configuration error"); kfree(new_cfg); return err; } kfree(new_cfg); /* update tx_real_queues */ otx2_qos_update_tx_netdev_queues(pfvf); return 0; } void otx2_clean_qos_queues(struct otx2_nic *pfvf) { struct otx2_qos_node *root; root = otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID); if (!root) return; otx2_qos_update_smq(pfvf, root, QOS_SMQ_FLUSH); } void otx2_qos_config_txschq(struct otx2_nic *pfvf) { struct otx2_qos_node *root; int err; root = otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID); if (!root) return; if (root->level != NIX_TXSCH_LVL_TL1) { err = otx2_qos_txschq_config(pfvf, root); if (err) { netdev_err(pfvf->netdev, "Error update txschq configuration\n"); goto root_destroy; } } err = otx2_qos_txschq_push_cfg_tl(pfvf, root, NULL); if (err) { netdev_err(pfvf->netdev, "Error update txschq configuration\n"); goto root_destroy; } otx2_qos_update_smq(pfvf, root, QOS_CFG_SQ); return; root_destroy: netdev_err(pfvf->netdev, "Failed to update Scheduler/Shaping config in Hardware\n"); /* Free resources allocated */ otx2_qos_root_destroy(pfvf); } int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb) { struct otx2_nic *pfvf = netdev_priv(ndev); int res; switch (htb->command) { case TC_HTB_CREATE: return otx2_qos_root_add(pfvf, htb->parent_classid, htb->classid, htb->extack); case TC_HTB_DESTROY: return otx2_qos_root_destroy(pfvf); case TC_HTB_LEAF_ALLOC_QUEUE: res = otx2_qos_leaf_alloc_queue(pfvf, htb->classid, htb->parent_classid, htb->rate, htb->ceil, htb->prio, htb->quantum, htb->extack); if (res < 0) return res; htb->qid = res; return 0; case TC_HTB_LEAF_TO_INNER: return otx2_qos_leaf_to_inner(pfvf, htb->parent_classid, htb->classid, htb->rate, htb->ceil, htb->prio, htb->quantum, htb->extack); case TC_HTB_LEAF_DEL: return otx2_qos_leaf_del(pfvf, &htb->classid, htb->extack); case TC_HTB_LEAF_DEL_LAST: case TC_HTB_LEAF_DEL_LAST_FORCE: return otx2_qos_leaf_del_last(pfvf, htb->classid, htb->command == TC_HTB_LEAF_DEL_LAST_FORCE, htb->extack); case TC_HTB_LEAF_QUERY_QUEUE: res = otx2_get_txq_by_classid(pfvf, htb->classid); htb->qid = res; return 0; case TC_HTB_NODE_MODIFY: fallthrough; default: return -EOPNOTSUPP; } }