162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Marvell RVU Ethernet driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2023 Marvell. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/netdevice.h> 862306a36Sopenharmony_ci#include <linux/etherdevice.h> 962306a36Sopenharmony_ci#include <linux/inetdevice.h> 1062306a36Sopenharmony_ci#include <linux/bitfield.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "otx2_common.h" 1362306a36Sopenharmony_ci#include "cn10k.h" 1462306a36Sopenharmony_ci#include "qos.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define OTX2_QOS_QID_INNER 0xFFFFU 1762306a36Sopenharmony_ci#define OTX2_QOS_QID_NONE 0xFFFEU 1862306a36Sopenharmony_ci#define OTX2_QOS_ROOT_CLASSID 0xFFFFFFFF 1962306a36Sopenharmony_ci#define OTX2_QOS_CLASS_NONE 0 2062306a36Sopenharmony_ci#define OTX2_QOS_DEFAULT_PRIO 0xF 2162306a36Sopenharmony_ci#define OTX2_QOS_INVALID_SQ 0xFFFF 2262306a36Sopenharmony_ci#define OTX2_QOS_INVALID_TXSCHQ_IDX 0xFFFF 2362306a36Sopenharmony_ci#define CN10K_MAX_RR_WEIGHT GENMASK_ULL(13, 0) 2462306a36Sopenharmony_ci#define OTX2_MAX_RR_QUANTUM GENMASK_ULL(23, 0) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void otx2_qos_update_tx_netdev_queues(struct otx2_nic *pfvf) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct otx2_hw *hw = &pfvf->hw; 2962306a36Sopenharmony_ci int tx_queues, qos_txqs, err; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci qos_txqs = bitmap_weight(pfvf->qos.qos_sq_bmap, 3262306a36Sopenharmony_ci OTX2_QOS_MAX_LEAF_NODES); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci tx_queues = hw->tx_queues + qos_txqs; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci err = netif_set_real_num_tx_queues(pfvf->netdev, tx_queues); 3762306a36Sopenharmony_ci if (err) { 3862306a36Sopenharmony_ci netdev_err(pfvf->netdev, 3962306a36Sopenharmony_ci "Failed to set no of Tx queues: %d\n", tx_queues); 4062306a36Sopenharmony_ci return; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void otx2_qos_get_regaddr(struct otx2_qos_node *node, 4562306a36Sopenharmony_ci struct nix_txschq_config *cfg, 4662306a36Sopenharmony_ci int index) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci if (node->level == NIX_TXSCH_LVL_SMQ) { 4962306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_MDQX_PARENT(node->schq); 5062306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_MDQX_SCHEDULE(node->schq); 5162306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_MDQX_PIR(node->schq); 5262306a36Sopenharmony_ci cfg->reg[index] = NIX_AF_MDQX_CIR(node->schq); 5362306a36Sopenharmony_ci } else if (node->level == NIX_TXSCH_LVL_TL4) { 5462306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_TL4X_PARENT(node->schq); 5562306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_TL4X_SCHEDULE(node->schq); 5662306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_TL4X_PIR(node->schq); 5762306a36Sopenharmony_ci cfg->reg[index] = NIX_AF_TL4X_CIR(node->schq); 5862306a36Sopenharmony_ci } else if (node->level == NIX_TXSCH_LVL_TL3) { 5962306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_TL3X_PARENT(node->schq); 6062306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_TL3X_SCHEDULE(node->schq); 6162306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_TL3X_PIR(node->schq); 6262306a36Sopenharmony_ci cfg->reg[index] = NIX_AF_TL3X_CIR(node->schq); 6362306a36Sopenharmony_ci } else if (node->level == NIX_TXSCH_LVL_TL2) { 6462306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_TL2X_PARENT(node->schq); 6562306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_TL2X_SCHEDULE(node->schq); 6662306a36Sopenharmony_ci cfg->reg[index++] = NIX_AF_TL2X_PIR(node->schq); 6762306a36Sopenharmony_ci cfg->reg[index] = NIX_AF_TL2X_CIR(node->schq); 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int otx2_qos_quantum_to_dwrr_weight(struct otx2_nic *pfvf, u32 quantum) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci u32 weight; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci weight = quantum / pfvf->hw.dwrr_mtu; 7662306a36Sopenharmony_ci if (quantum % pfvf->hw.dwrr_mtu) 7762306a36Sopenharmony_ci weight += 1; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return weight; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void otx2_config_sched_shaping(struct otx2_nic *pfvf, 8362306a36Sopenharmony_ci struct otx2_qos_node *node, 8462306a36Sopenharmony_ci struct nix_txschq_config *cfg, 8562306a36Sopenharmony_ci int *num_regs) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci u32 rr_weight; 8862306a36Sopenharmony_ci u32 quantum; 8962306a36Sopenharmony_ci u64 maxrate; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci otx2_qos_get_regaddr(node, cfg, *num_regs); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* configure parent txschq */ 9462306a36Sopenharmony_ci cfg->regval[*num_regs] = node->parent->schq << 16; 9562306a36Sopenharmony_ci (*num_regs)++; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* configure prio/quantum */ 9862306a36Sopenharmony_ci if (node->qid == OTX2_QOS_QID_NONE) { 9962306a36Sopenharmony_ci cfg->regval[*num_regs] = node->prio << 24 | 10062306a36Sopenharmony_ci mtu_to_dwrr_weight(pfvf, pfvf->tx_max_pktlen); 10162306a36Sopenharmony_ci (*num_regs)++; 10262306a36Sopenharmony_ci return; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* configure priority/quantum */ 10662306a36Sopenharmony_ci if (node->is_static) { 10762306a36Sopenharmony_ci cfg->regval[*num_regs] = 10862306a36Sopenharmony_ci (node->schq - node->parent->prio_anchor) << 24; 10962306a36Sopenharmony_ci } else { 11062306a36Sopenharmony_ci quantum = node->quantum ? 11162306a36Sopenharmony_ci node->quantum : pfvf->tx_max_pktlen; 11262306a36Sopenharmony_ci rr_weight = otx2_qos_quantum_to_dwrr_weight(pfvf, quantum); 11362306a36Sopenharmony_ci cfg->regval[*num_regs] = node->parent->child_dwrr_prio << 24 | 11462306a36Sopenharmony_ci rr_weight; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci (*num_regs)++; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* configure PIR */ 11962306a36Sopenharmony_ci maxrate = (node->rate > node->ceil) ? node->rate : node->ceil; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci cfg->regval[*num_regs] = 12262306a36Sopenharmony_ci otx2_get_txschq_rate_regval(pfvf, maxrate, 65536); 12362306a36Sopenharmony_ci (*num_regs)++; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Don't configure CIR when both CIR+PIR not supported 12662306a36Sopenharmony_ci * On 96xx, CIR + PIR + RED_ALGO=STALL causes deadlock 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci if (!test_bit(QOS_CIR_PIR_SUPPORT, &pfvf->hw.cap_flag)) 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci cfg->regval[*num_regs] = 13262306a36Sopenharmony_ci otx2_get_txschq_rate_regval(pfvf, node->rate, 65536); 13362306a36Sopenharmony_ci (*num_regs)++; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void __otx2_qos_txschq_cfg(struct otx2_nic *pfvf, 13762306a36Sopenharmony_ci struct otx2_qos_node *node, 13862306a36Sopenharmony_ci struct nix_txschq_config *cfg) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct otx2_hw *hw = &pfvf->hw; 14162306a36Sopenharmony_ci int num_regs = 0; 14262306a36Sopenharmony_ci u8 level; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci level = node->level; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* program txschq registers */ 14762306a36Sopenharmony_ci if (level == NIX_TXSCH_LVL_SMQ) { 14862306a36Sopenharmony_ci cfg->reg[num_regs] = NIX_AF_SMQX_CFG(node->schq); 14962306a36Sopenharmony_ci cfg->regval[num_regs] = ((u64)pfvf->tx_max_pktlen << 8) | 15062306a36Sopenharmony_ci OTX2_MIN_MTU; 15162306a36Sopenharmony_ci cfg->regval[num_regs] |= (0x20ULL << 51) | (0x80ULL << 39) | 15262306a36Sopenharmony_ci (0x2ULL << 36); 15362306a36Sopenharmony_ci num_regs++; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci } else if (level == NIX_TXSCH_LVL_TL4) { 15862306a36Sopenharmony_ci otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); 15962306a36Sopenharmony_ci } else if (level == NIX_TXSCH_LVL_TL3) { 16062306a36Sopenharmony_ci /* configure link cfg */ 16162306a36Sopenharmony_ci if (level == pfvf->qos.link_cfg_lvl) { 16262306a36Sopenharmony_ci cfg->reg[num_regs] = NIX_AF_TL3_TL2X_LINKX_CFG(node->schq, hw->tx_link); 16362306a36Sopenharmony_ci cfg->regval[num_regs] = BIT_ULL(13) | BIT_ULL(12); 16462306a36Sopenharmony_ci num_regs++; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); 16862306a36Sopenharmony_ci } else if (level == NIX_TXSCH_LVL_TL2) { 16962306a36Sopenharmony_ci /* configure link cfg */ 17062306a36Sopenharmony_ci if (level == pfvf->qos.link_cfg_lvl) { 17162306a36Sopenharmony_ci cfg->reg[num_regs] = NIX_AF_TL3_TL2X_LINKX_CFG(node->schq, hw->tx_link); 17262306a36Sopenharmony_ci cfg->regval[num_regs] = BIT_ULL(13) | BIT_ULL(12); 17362306a36Sopenharmony_ci num_regs++; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* check if node is root */ 17762306a36Sopenharmony_ci if (node->qid == OTX2_QOS_QID_INNER && !node->parent) { 17862306a36Sopenharmony_ci cfg->reg[num_regs] = NIX_AF_TL2X_SCHEDULE(node->schq); 17962306a36Sopenharmony_ci cfg->regval[num_regs] = TXSCH_TL1_DFLT_RR_PRIO << 24 | 18062306a36Sopenharmony_ci mtu_to_dwrr_weight(pfvf, 18162306a36Sopenharmony_ci pfvf->tx_max_pktlen); 18262306a36Sopenharmony_ci num_regs++; 18362306a36Sopenharmony_ci goto txschq_cfg_out; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_citxschq_cfg_out: 19062306a36Sopenharmony_ci cfg->num_regs = num_regs; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int otx2_qos_txschq_set_parent_topology(struct otx2_nic *pfvf, 19462306a36Sopenharmony_ci struct otx2_qos_node *parent) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct mbox *mbox = &pfvf->mbox; 19762306a36Sopenharmony_ci struct nix_txschq_config *cfg; 19862306a36Sopenharmony_ci int rc; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (parent->level == NIX_TXSCH_LVL_MDQ) 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci mutex_lock(&mbox->lock); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci cfg = otx2_mbox_alloc_msg_nix_txschq_cfg(&pfvf->mbox); 20662306a36Sopenharmony_ci if (!cfg) { 20762306a36Sopenharmony_ci mutex_unlock(&mbox->lock); 20862306a36Sopenharmony_ci return -ENOMEM; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci cfg->lvl = parent->level; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (parent->level == NIX_TXSCH_LVL_TL4) 21462306a36Sopenharmony_ci cfg->reg[0] = NIX_AF_TL4X_TOPOLOGY(parent->schq); 21562306a36Sopenharmony_ci else if (parent->level == NIX_TXSCH_LVL_TL3) 21662306a36Sopenharmony_ci cfg->reg[0] = NIX_AF_TL3X_TOPOLOGY(parent->schq); 21762306a36Sopenharmony_ci else if (parent->level == NIX_TXSCH_LVL_TL2) 21862306a36Sopenharmony_ci cfg->reg[0] = NIX_AF_TL2X_TOPOLOGY(parent->schq); 21962306a36Sopenharmony_ci else if (parent->level == NIX_TXSCH_LVL_TL1) 22062306a36Sopenharmony_ci cfg->reg[0] = NIX_AF_TL1X_TOPOLOGY(parent->schq); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci cfg->regval[0] = (u64)parent->prio_anchor << 32; 22362306a36Sopenharmony_ci cfg->regval[0] |= ((parent->child_dwrr_prio != OTX2_QOS_DEFAULT_PRIO) ? 22462306a36Sopenharmony_ci parent->child_dwrr_prio : 0) << 1; 22562306a36Sopenharmony_ci cfg->num_regs++; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci rc = otx2_sync_mbox_msg(&pfvf->mbox); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci mutex_unlock(&mbox->lock); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return rc; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic void otx2_qos_free_hw_node_schq(struct otx2_nic *pfvf, 23562306a36Sopenharmony_ci struct otx2_qos_node *parent) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct otx2_qos_node *node; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci list_for_each_entry_reverse(node, &parent->child_schq_list, list) 24062306a36Sopenharmony_ci otx2_txschq_free_one(pfvf, node->level, node->schq); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic void otx2_qos_free_hw_node(struct otx2_nic *pfvf, 24462306a36Sopenharmony_ci struct otx2_qos_node *parent) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct otx2_qos_node *node, *tmp; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci list_for_each_entry_safe(node, tmp, &parent->child_list, list) { 24962306a36Sopenharmony_ci otx2_qos_free_hw_node(pfvf, node); 25062306a36Sopenharmony_ci otx2_qos_free_hw_node_schq(pfvf, node); 25162306a36Sopenharmony_ci otx2_txschq_free_one(pfvf, node->level, node->schq); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic void otx2_qos_free_hw_cfg(struct otx2_nic *pfvf, 25662306a36Sopenharmony_ci struct otx2_qos_node *node) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci mutex_lock(&pfvf->qos.qos_lock); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* free child node hw mappings */ 26162306a36Sopenharmony_ci otx2_qos_free_hw_node(pfvf, node); 26262306a36Sopenharmony_ci otx2_qos_free_hw_node_schq(pfvf, node); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* free node hw mappings */ 26562306a36Sopenharmony_ci otx2_txschq_free_one(pfvf, node->level, node->schq); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void otx2_qos_sw_node_delete(struct otx2_nic *pfvf, 27162306a36Sopenharmony_ci struct otx2_qos_node *node) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci hash_del_rcu(&node->hlist); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (node->qid != OTX2_QOS_QID_INNER && node->qid != OTX2_QOS_QID_NONE) { 27662306a36Sopenharmony_ci __clear_bit(node->qid, pfvf->qos.qos_sq_bmap); 27762306a36Sopenharmony_ci otx2_qos_update_tx_netdev_queues(pfvf); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci list_del(&node->list); 28162306a36Sopenharmony_ci kfree(node); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void otx2_qos_free_sw_node_schq(struct otx2_nic *pfvf, 28562306a36Sopenharmony_ci struct otx2_qos_node *parent) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct otx2_qos_node *node, *tmp; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci list_for_each_entry_safe(node, tmp, &parent->child_schq_list, list) { 29062306a36Sopenharmony_ci list_del(&node->list); 29162306a36Sopenharmony_ci kfree(node); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void __otx2_qos_free_sw_node(struct otx2_nic *pfvf, 29662306a36Sopenharmony_ci struct otx2_qos_node *parent) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct otx2_qos_node *node, *tmp; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci list_for_each_entry_safe(node, tmp, &parent->child_list, list) { 30162306a36Sopenharmony_ci __otx2_qos_free_sw_node(pfvf, node); 30262306a36Sopenharmony_ci otx2_qos_free_sw_node_schq(pfvf, node); 30362306a36Sopenharmony_ci otx2_qos_sw_node_delete(pfvf, node); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void otx2_qos_free_sw_node(struct otx2_nic *pfvf, 30862306a36Sopenharmony_ci struct otx2_qos_node *node) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci mutex_lock(&pfvf->qos.qos_lock); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci __otx2_qos_free_sw_node(pfvf, node); 31362306a36Sopenharmony_ci otx2_qos_free_sw_node_schq(pfvf, node); 31462306a36Sopenharmony_ci otx2_qos_sw_node_delete(pfvf, node); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void otx2_qos_destroy_node(struct otx2_nic *pfvf, 32062306a36Sopenharmony_ci struct otx2_qos_node *node) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci otx2_qos_free_hw_cfg(pfvf, node); 32362306a36Sopenharmony_ci otx2_qos_free_sw_node(pfvf, node); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void otx2_qos_fill_cfg_schq(struct otx2_qos_node *parent, 32762306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct otx2_qos_node *node; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci list_for_each_entry(node, &parent->child_schq_list, list) 33262306a36Sopenharmony_ci cfg->schq[node->level]++; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic void otx2_qos_fill_cfg_tl(struct otx2_qos_node *parent, 33662306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct otx2_qos_node *node; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci list_for_each_entry(node, &parent->child_list, list) { 34162306a36Sopenharmony_ci otx2_qos_fill_cfg_tl(node, cfg); 34262306a36Sopenharmony_ci otx2_qos_fill_cfg_schq(node, cfg); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Assign the required number of transmit schedular queues under the 34662306a36Sopenharmony_ci * given class 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_ci cfg->schq_contig[parent->level - 1] += parent->child_dwrr_cnt + 34962306a36Sopenharmony_ci parent->max_static_prio + 1; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void otx2_qos_prepare_txschq_cfg(struct otx2_nic *pfvf, 35362306a36Sopenharmony_ci struct otx2_qos_node *parent, 35462306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci mutex_lock(&pfvf->qos.qos_lock); 35762306a36Sopenharmony_ci otx2_qos_fill_cfg_tl(parent, cfg); 35862306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic void otx2_qos_read_txschq_cfg_schq(struct otx2_qos_node *parent, 36262306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct otx2_qos_node *node; 36562306a36Sopenharmony_ci int cnt; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci list_for_each_entry(node, &parent->child_schq_list, list) { 36862306a36Sopenharmony_ci cnt = cfg->dwrr_node_pos[node->level]; 36962306a36Sopenharmony_ci cfg->schq_list[node->level][cnt] = node->schq; 37062306a36Sopenharmony_ci cfg->schq[node->level]++; 37162306a36Sopenharmony_ci cfg->dwrr_node_pos[node->level]++; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void otx2_qos_read_txschq_cfg_tl(struct otx2_qos_node *parent, 37662306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct otx2_qos_node *node; 37962306a36Sopenharmony_ci int cnt; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci list_for_each_entry(node, &parent->child_list, list) { 38262306a36Sopenharmony_ci otx2_qos_read_txschq_cfg_tl(node, cfg); 38362306a36Sopenharmony_ci cnt = cfg->static_node_pos[node->level]; 38462306a36Sopenharmony_ci cfg->schq_contig_list[node->level][cnt] = node->schq; 38562306a36Sopenharmony_ci cfg->schq_contig[node->level]++; 38662306a36Sopenharmony_ci cfg->static_node_pos[node->level]++; 38762306a36Sopenharmony_ci otx2_qos_read_txschq_cfg_schq(node, cfg); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic void otx2_qos_read_txschq_cfg(struct otx2_nic *pfvf, 39262306a36Sopenharmony_ci struct otx2_qos_node *node, 39362306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci mutex_lock(&pfvf->qos.qos_lock); 39662306a36Sopenharmony_ci otx2_qos_read_txschq_cfg_tl(node, cfg); 39762306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic struct otx2_qos_node * 40162306a36Sopenharmony_ciotx2_qos_alloc_root(struct otx2_nic *pfvf) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct otx2_qos_node *node; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci node = kzalloc(sizeof(*node), GFP_KERNEL); 40662306a36Sopenharmony_ci if (!node) 40762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci node->parent = NULL; 41062306a36Sopenharmony_ci if (!is_otx2_vf(pfvf->pcifunc)) { 41162306a36Sopenharmony_ci node->level = NIX_TXSCH_LVL_TL1; 41262306a36Sopenharmony_ci } else { 41362306a36Sopenharmony_ci node->level = NIX_TXSCH_LVL_TL2; 41462306a36Sopenharmony_ci node->child_dwrr_prio = OTX2_QOS_DEFAULT_PRIO; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci WRITE_ONCE(node->qid, OTX2_QOS_QID_INNER); 41862306a36Sopenharmony_ci node->classid = OTX2_QOS_ROOT_CLASSID; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci hash_add_rcu(pfvf->qos.qos_hlist, &node->hlist, node->classid); 42162306a36Sopenharmony_ci list_add_tail(&node->list, &pfvf->qos.qos_tree); 42262306a36Sopenharmony_ci INIT_LIST_HEAD(&node->child_list); 42362306a36Sopenharmony_ci INIT_LIST_HEAD(&node->child_schq_list); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return node; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int otx2_qos_add_child_node(struct otx2_qos_node *parent, 42962306a36Sopenharmony_ci struct otx2_qos_node *node) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct list_head *head = &parent->child_list; 43262306a36Sopenharmony_ci struct otx2_qos_node *tmp_node; 43362306a36Sopenharmony_ci struct list_head *tmp; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (node->prio > parent->max_static_prio) 43662306a36Sopenharmony_ci parent->max_static_prio = node->prio; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci for (tmp = head->next; tmp != head; tmp = tmp->next) { 43962306a36Sopenharmony_ci tmp_node = list_entry(tmp, struct otx2_qos_node, list); 44062306a36Sopenharmony_ci if (tmp_node->prio == node->prio && 44162306a36Sopenharmony_ci tmp_node->is_static) 44262306a36Sopenharmony_ci return -EEXIST; 44362306a36Sopenharmony_ci if (tmp_node->prio > node->prio) { 44462306a36Sopenharmony_ci list_add_tail(&node->list, tmp); 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci list_add_tail(&node->list, head); 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic int otx2_qos_alloc_txschq_node(struct otx2_nic *pfvf, 45462306a36Sopenharmony_ci struct otx2_qos_node *node) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct otx2_qos_node *txschq_node, *parent, *tmp; 45762306a36Sopenharmony_ci int lvl; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci parent = node; 46062306a36Sopenharmony_ci for (lvl = node->level - 1; lvl >= NIX_TXSCH_LVL_MDQ; lvl--) { 46162306a36Sopenharmony_ci txschq_node = kzalloc(sizeof(*txschq_node), GFP_KERNEL); 46262306a36Sopenharmony_ci if (!txschq_node) 46362306a36Sopenharmony_ci goto err_out; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci txschq_node->parent = parent; 46662306a36Sopenharmony_ci txschq_node->level = lvl; 46762306a36Sopenharmony_ci txschq_node->classid = OTX2_QOS_CLASS_NONE; 46862306a36Sopenharmony_ci WRITE_ONCE(txschq_node->qid, OTX2_QOS_QID_NONE); 46962306a36Sopenharmony_ci txschq_node->rate = 0; 47062306a36Sopenharmony_ci txschq_node->ceil = 0; 47162306a36Sopenharmony_ci txschq_node->prio = 0; 47262306a36Sopenharmony_ci txschq_node->quantum = 0; 47362306a36Sopenharmony_ci txschq_node->is_static = true; 47462306a36Sopenharmony_ci txschq_node->child_dwrr_prio = OTX2_QOS_DEFAULT_PRIO; 47562306a36Sopenharmony_ci txschq_node->txschq_idx = OTX2_QOS_INVALID_TXSCHQ_IDX; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci mutex_lock(&pfvf->qos.qos_lock); 47862306a36Sopenharmony_ci list_add_tail(&txschq_node->list, &node->child_schq_list); 47962306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci INIT_LIST_HEAD(&txschq_node->child_list); 48262306a36Sopenharmony_ci INIT_LIST_HEAD(&txschq_node->child_schq_list); 48362306a36Sopenharmony_ci parent = txschq_node; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cierr_out: 48962306a36Sopenharmony_ci list_for_each_entry_safe(txschq_node, tmp, &node->child_schq_list, 49062306a36Sopenharmony_ci list) { 49162306a36Sopenharmony_ci list_del(&txschq_node->list); 49262306a36Sopenharmony_ci kfree(txschq_node); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci return -ENOMEM; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic struct otx2_qos_node * 49862306a36Sopenharmony_ciotx2_qos_sw_create_leaf_node(struct otx2_nic *pfvf, 49962306a36Sopenharmony_ci struct otx2_qos_node *parent, 50062306a36Sopenharmony_ci u16 classid, u32 prio, u64 rate, u64 ceil, 50162306a36Sopenharmony_ci u32 quantum, u16 qid, bool static_cfg) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct otx2_qos_node *node; 50462306a36Sopenharmony_ci int err; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci node = kzalloc(sizeof(*node), GFP_KERNEL); 50762306a36Sopenharmony_ci if (!node) 50862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci node->parent = parent; 51162306a36Sopenharmony_ci node->level = parent->level - 1; 51262306a36Sopenharmony_ci node->classid = classid; 51362306a36Sopenharmony_ci WRITE_ONCE(node->qid, qid); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci node->rate = otx2_convert_rate(rate); 51662306a36Sopenharmony_ci node->ceil = otx2_convert_rate(ceil); 51762306a36Sopenharmony_ci node->prio = prio; 51862306a36Sopenharmony_ci node->quantum = quantum; 51962306a36Sopenharmony_ci node->is_static = static_cfg; 52062306a36Sopenharmony_ci node->child_dwrr_prio = OTX2_QOS_DEFAULT_PRIO; 52162306a36Sopenharmony_ci node->txschq_idx = OTX2_QOS_INVALID_TXSCHQ_IDX; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci __set_bit(qid, pfvf->qos.qos_sq_bmap); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci hash_add_rcu(pfvf->qos.qos_hlist, &node->hlist, classid); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci mutex_lock(&pfvf->qos.qos_lock); 52862306a36Sopenharmony_ci err = otx2_qos_add_child_node(parent, node); 52962306a36Sopenharmony_ci if (err) { 53062306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 53162306a36Sopenharmony_ci return ERR_PTR(err); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci INIT_LIST_HEAD(&node->child_list); 53662306a36Sopenharmony_ci INIT_LIST_HEAD(&node->child_schq_list); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci err = otx2_qos_alloc_txschq_node(pfvf, node); 53962306a36Sopenharmony_ci if (err) { 54062306a36Sopenharmony_ci otx2_qos_sw_node_delete(pfvf, node); 54162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return node; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic struct otx2_qos_node * 54862306a36Sopenharmony_ciotx2_sw_node_find(struct otx2_nic *pfvf, u32 classid) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct otx2_qos_node *node = NULL; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci hash_for_each_possible(pfvf->qos.qos_hlist, node, hlist, classid) { 55362306a36Sopenharmony_ci if (node->classid == classid) 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return node; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic struct otx2_qos_node * 56162306a36Sopenharmony_ciotx2_sw_node_find_rcu(struct otx2_nic *pfvf, u32 classid) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct otx2_qos_node *node = NULL; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci hash_for_each_possible_rcu(pfvf->qos.qos_hlist, node, hlist, classid) { 56662306a36Sopenharmony_ci if (node->classid == classid) 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return node; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ciint otx2_get_txq_by_classid(struct otx2_nic *pfvf, u16 classid) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct otx2_qos_node *node; 57662306a36Sopenharmony_ci u16 qid; 57762306a36Sopenharmony_ci int res; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci node = otx2_sw_node_find_rcu(pfvf, classid); 58062306a36Sopenharmony_ci if (!node) { 58162306a36Sopenharmony_ci res = -ENOENT; 58262306a36Sopenharmony_ci goto out; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci qid = READ_ONCE(node->qid); 58562306a36Sopenharmony_ci if (qid == OTX2_QOS_QID_INNER) { 58662306a36Sopenharmony_ci res = -EINVAL; 58762306a36Sopenharmony_ci goto out; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci res = pfvf->hw.tx_queues + qid; 59062306a36Sopenharmony_ciout: 59162306a36Sopenharmony_ci return res; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic int 59562306a36Sopenharmony_ciotx2_qos_txschq_config(struct otx2_nic *pfvf, struct otx2_qos_node *node) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct mbox *mbox = &pfvf->mbox; 59862306a36Sopenharmony_ci struct nix_txschq_config *req; 59962306a36Sopenharmony_ci int rc; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci mutex_lock(&mbox->lock); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci req = otx2_mbox_alloc_msg_nix_txschq_cfg(&pfvf->mbox); 60462306a36Sopenharmony_ci if (!req) { 60562306a36Sopenharmony_ci mutex_unlock(&mbox->lock); 60662306a36Sopenharmony_ci return -ENOMEM; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci req->lvl = node->level; 61062306a36Sopenharmony_ci __otx2_qos_txschq_cfg(pfvf, node, req); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci rc = otx2_sync_mbox_msg(&pfvf->mbox); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci mutex_unlock(&mbox->lock); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return rc; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int otx2_qos_txschq_alloc(struct otx2_nic *pfvf, 62062306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct nix_txsch_alloc_req *req; 62362306a36Sopenharmony_ci struct nix_txsch_alloc_rsp *rsp; 62462306a36Sopenharmony_ci struct mbox *mbox = &pfvf->mbox; 62562306a36Sopenharmony_ci int lvl, rc, schq; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci mutex_lock(&mbox->lock); 62862306a36Sopenharmony_ci req = otx2_mbox_alloc_msg_nix_txsch_alloc(&pfvf->mbox); 62962306a36Sopenharmony_ci if (!req) { 63062306a36Sopenharmony_ci mutex_unlock(&mbox->lock); 63162306a36Sopenharmony_ci return -ENOMEM; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { 63562306a36Sopenharmony_ci req->schq[lvl] = cfg->schq[lvl]; 63662306a36Sopenharmony_ci req->schq_contig[lvl] = cfg->schq_contig[lvl]; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci rc = otx2_sync_mbox_msg(&pfvf->mbox); 64062306a36Sopenharmony_ci if (rc) { 64162306a36Sopenharmony_ci mutex_unlock(&mbox->lock); 64262306a36Sopenharmony_ci return rc; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci rsp = (struct nix_txsch_alloc_rsp *) 64662306a36Sopenharmony_ci otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (IS_ERR(rsp)) { 64962306a36Sopenharmony_ci rc = PTR_ERR(rsp); 65062306a36Sopenharmony_ci goto out; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { 65462306a36Sopenharmony_ci for (schq = 0; schq < rsp->schq_contig[lvl]; schq++) { 65562306a36Sopenharmony_ci cfg->schq_contig_list[lvl][schq] = 65662306a36Sopenharmony_ci rsp->schq_contig_list[lvl][schq]; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { 66162306a36Sopenharmony_ci for (schq = 0; schq < rsp->schq[lvl]; schq++) { 66262306a36Sopenharmony_ci cfg->schq_list[lvl][schq] = 66362306a36Sopenharmony_ci rsp->schq_list[lvl][schq]; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci pfvf->qos.link_cfg_lvl = rsp->link_cfg_lvl; 66862306a36Sopenharmony_ci pfvf->hw.txschq_aggr_lvl_rr_prio = rsp->aggr_lvl_rr_prio; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ciout: 67162306a36Sopenharmony_ci mutex_unlock(&mbox->lock); 67262306a36Sopenharmony_ci return rc; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic void otx2_qos_free_unused_txschq(struct otx2_nic *pfvf, 67662306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci int lvl, idx, schq; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { 68162306a36Sopenharmony_ci for (idx = 0; idx < cfg->schq_contig[lvl]; idx++) { 68262306a36Sopenharmony_ci if (!cfg->schq_index_used[lvl][idx]) { 68362306a36Sopenharmony_ci schq = cfg->schq_contig_list[lvl][idx]; 68462306a36Sopenharmony_ci otx2_txschq_free_one(pfvf, lvl, schq); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic void otx2_qos_txschq_fill_cfg_schq(struct otx2_nic *pfvf, 69162306a36Sopenharmony_ci struct otx2_qos_node *node, 69262306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct otx2_qos_node *tmp; 69562306a36Sopenharmony_ci int cnt; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci list_for_each_entry(tmp, &node->child_schq_list, list) { 69862306a36Sopenharmony_ci cnt = cfg->dwrr_node_pos[tmp->level]; 69962306a36Sopenharmony_ci tmp->schq = cfg->schq_list[tmp->level][cnt]; 70062306a36Sopenharmony_ci cfg->dwrr_node_pos[tmp->level]++; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic void otx2_qos_txschq_fill_cfg_tl(struct otx2_nic *pfvf, 70562306a36Sopenharmony_ci struct otx2_qos_node *node, 70662306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct otx2_qos_node *tmp; 70962306a36Sopenharmony_ci int cnt; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci list_for_each_entry(tmp, &node->child_list, list) { 71262306a36Sopenharmony_ci otx2_qos_txschq_fill_cfg_tl(pfvf, tmp, cfg); 71362306a36Sopenharmony_ci cnt = cfg->static_node_pos[tmp->level]; 71462306a36Sopenharmony_ci tmp->schq = cfg->schq_contig_list[tmp->level][tmp->txschq_idx]; 71562306a36Sopenharmony_ci cfg->schq_index_used[tmp->level][tmp->txschq_idx] = true; 71662306a36Sopenharmony_ci if (cnt == 0) 71762306a36Sopenharmony_ci node->prio_anchor = 71862306a36Sopenharmony_ci cfg->schq_contig_list[tmp->level][0]; 71962306a36Sopenharmony_ci cfg->static_node_pos[tmp->level]++; 72062306a36Sopenharmony_ci otx2_qos_txschq_fill_cfg_schq(pfvf, tmp, cfg); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic void otx2_qos_txschq_fill_cfg(struct otx2_nic *pfvf, 72562306a36Sopenharmony_ci struct otx2_qos_node *node, 72662306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci mutex_lock(&pfvf->qos.qos_lock); 72962306a36Sopenharmony_ci otx2_qos_txschq_fill_cfg_tl(pfvf, node, cfg); 73062306a36Sopenharmony_ci otx2_qos_txschq_fill_cfg_schq(pfvf, node, cfg); 73162306a36Sopenharmony_ci otx2_qos_free_unused_txschq(pfvf, cfg); 73262306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic void __otx2_qos_assign_base_idx_tl(struct otx2_nic *pfvf, 73662306a36Sopenharmony_ci struct otx2_qos_node *tmp, 73762306a36Sopenharmony_ci unsigned long *child_idx_bmap, 73862306a36Sopenharmony_ci int child_cnt) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci int idx; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (tmp->txschq_idx != OTX2_QOS_INVALID_TXSCHQ_IDX) 74362306a36Sopenharmony_ci return; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* assign static nodes 1:1 prio mapping first, then remaining nodes */ 74662306a36Sopenharmony_ci for (idx = 0; idx < child_cnt; idx++) { 74762306a36Sopenharmony_ci if (tmp->is_static && tmp->prio == idx && 74862306a36Sopenharmony_ci !test_bit(idx, child_idx_bmap)) { 74962306a36Sopenharmony_ci tmp->txschq_idx = idx; 75062306a36Sopenharmony_ci set_bit(idx, child_idx_bmap); 75162306a36Sopenharmony_ci return; 75262306a36Sopenharmony_ci } else if (!tmp->is_static && idx >= tmp->prio && 75362306a36Sopenharmony_ci !test_bit(idx, child_idx_bmap)) { 75462306a36Sopenharmony_ci tmp->txschq_idx = idx; 75562306a36Sopenharmony_ci set_bit(idx, child_idx_bmap); 75662306a36Sopenharmony_ci return; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic int otx2_qos_assign_base_idx_tl(struct otx2_nic *pfvf, 76262306a36Sopenharmony_ci struct otx2_qos_node *node) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci unsigned long *child_idx_bmap; 76562306a36Sopenharmony_ci struct otx2_qos_node *tmp; 76662306a36Sopenharmony_ci int child_cnt; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci list_for_each_entry(tmp, &node->child_list, list) 76962306a36Sopenharmony_ci tmp->txschq_idx = OTX2_QOS_INVALID_TXSCHQ_IDX; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* allocate child index array */ 77262306a36Sopenharmony_ci child_cnt = node->child_dwrr_cnt + node->max_static_prio + 1; 77362306a36Sopenharmony_ci child_idx_bmap = kcalloc(BITS_TO_LONGS(child_cnt), 77462306a36Sopenharmony_ci sizeof(unsigned long), 77562306a36Sopenharmony_ci GFP_KERNEL); 77662306a36Sopenharmony_ci if (!child_idx_bmap) 77762306a36Sopenharmony_ci return -ENOMEM; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci list_for_each_entry(tmp, &node->child_list, list) 78062306a36Sopenharmony_ci otx2_qos_assign_base_idx_tl(pfvf, tmp); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* assign base index of static priority children first */ 78362306a36Sopenharmony_ci list_for_each_entry(tmp, &node->child_list, list) { 78462306a36Sopenharmony_ci if (!tmp->is_static) 78562306a36Sopenharmony_ci continue; 78662306a36Sopenharmony_ci __otx2_qos_assign_base_idx_tl(pfvf, tmp, child_idx_bmap, 78762306a36Sopenharmony_ci child_cnt); 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* assign base index of dwrr priority children */ 79162306a36Sopenharmony_ci list_for_each_entry(tmp, &node->child_list, list) 79262306a36Sopenharmony_ci __otx2_qos_assign_base_idx_tl(pfvf, tmp, child_idx_bmap, 79362306a36Sopenharmony_ci child_cnt); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci kfree(child_idx_bmap); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return 0; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int otx2_qos_assign_base_idx(struct otx2_nic *pfvf, 80162306a36Sopenharmony_ci struct otx2_qos_node *node) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci int ret = 0; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci mutex_lock(&pfvf->qos.qos_lock); 80662306a36Sopenharmony_ci ret = otx2_qos_assign_base_idx_tl(pfvf, node); 80762306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci return ret; 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic int otx2_qos_txschq_push_cfg_schq(struct otx2_nic *pfvf, 81362306a36Sopenharmony_ci struct otx2_qos_node *node, 81462306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct otx2_qos_node *tmp; 81762306a36Sopenharmony_ci int ret; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci list_for_each_entry(tmp, &node->child_schq_list, list) { 82062306a36Sopenharmony_ci ret = otx2_qos_txschq_config(pfvf, tmp); 82162306a36Sopenharmony_ci if (ret) 82262306a36Sopenharmony_ci return -EIO; 82362306a36Sopenharmony_ci ret = otx2_qos_txschq_set_parent_topology(pfvf, tmp->parent); 82462306a36Sopenharmony_ci if (ret) 82562306a36Sopenharmony_ci return -EIO; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci return 0; 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cistatic int otx2_qos_txschq_push_cfg_tl(struct otx2_nic *pfvf, 83262306a36Sopenharmony_ci struct otx2_qos_node *node, 83362306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct otx2_qos_node *tmp; 83662306a36Sopenharmony_ci int ret; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci list_for_each_entry(tmp, &node->child_list, list) { 83962306a36Sopenharmony_ci ret = otx2_qos_txschq_push_cfg_tl(pfvf, tmp, cfg); 84062306a36Sopenharmony_ci if (ret) 84162306a36Sopenharmony_ci return -EIO; 84262306a36Sopenharmony_ci ret = otx2_qos_txschq_config(pfvf, tmp); 84362306a36Sopenharmony_ci if (ret) 84462306a36Sopenharmony_ci return -EIO; 84562306a36Sopenharmony_ci ret = otx2_qos_txschq_push_cfg_schq(pfvf, tmp, cfg); 84662306a36Sopenharmony_ci if (ret) 84762306a36Sopenharmony_ci return -EIO; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci ret = otx2_qos_txschq_set_parent_topology(pfvf, node); 85162306a36Sopenharmony_ci if (ret) 85262306a36Sopenharmony_ci return -EIO; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci return 0; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic int otx2_qos_txschq_push_cfg(struct otx2_nic *pfvf, 85862306a36Sopenharmony_ci struct otx2_qos_node *node, 85962306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci int ret; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci mutex_lock(&pfvf->qos.qos_lock); 86462306a36Sopenharmony_ci ret = otx2_qos_txschq_push_cfg_tl(pfvf, node, cfg); 86562306a36Sopenharmony_ci if (ret) 86662306a36Sopenharmony_ci goto out; 86762306a36Sopenharmony_ci ret = otx2_qos_txschq_push_cfg_schq(pfvf, node, cfg); 86862306a36Sopenharmony_ciout: 86962306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 87062306a36Sopenharmony_ci return ret; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic int otx2_qos_txschq_update_config(struct otx2_nic *pfvf, 87462306a36Sopenharmony_ci struct otx2_qos_node *node, 87562306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci otx2_qos_txschq_fill_cfg(pfvf, node, cfg); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return otx2_qos_txschq_push_cfg(pfvf, node, cfg); 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic int otx2_qos_txschq_update_root_cfg(struct otx2_nic *pfvf, 88362306a36Sopenharmony_ci struct otx2_qos_node *root, 88462306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci root->schq = cfg->schq_list[root->level][0]; 88762306a36Sopenharmony_ci return otx2_qos_txschq_config(pfvf, root); 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic void otx2_qos_free_cfg(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci int lvl, idx, schq; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { 89562306a36Sopenharmony_ci for (idx = 0; idx < cfg->schq[lvl]; idx++) { 89662306a36Sopenharmony_ci schq = cfg->schq_list[lvl][idx]; 89762306a36Sopenharmony_ci otx2_txschq_free_one(pfvf, lvl, schq); 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { 90262306a36Sopenharmony_ci for (idx = 0; idx < cfg->schq_contig[lvl]; idx++) { 90362306a36Sopenharmony_ci if (cfg->schq_index_used[lvl][idx]) { 90462306a36Sopenharmony_ci schq = cfg->schq_contig_list[lvl][idx]; 90562306a36Sopenharmony_ci otx2_txschq_free_one(pfvf, lvl, schq); 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic void otx2_qos_enadis_sq(struct otx2_nic *pfvf, 91262306a36Sopenharmony_ci struct otx2_qos_node *node, 91362306a36Sopenharmony_ci u16 qid) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci if (pfvf->qos.qid_to_sqmap[qid] != OTX2_QOS_INVALID_SQ) 91662306a36Sopenharmony_ci otx2_qos_disable_sq(pfvf, qid); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci pfvf->qos.qid_to_sqmap[qid] = node->schq; 91962306a36Sopenharmony_ci otx2_qos_enable_sq(pfvf, qid); 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic void otx2_qos_update_smq_schq(struct otx2_nic *pfvf, 92362306a36Sopenharmony_ci struct otx2_qos_node *node, 92462306a36Sopenharmony_ci bool action) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct otx2_qos_node *tmp; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (node->qid == OTX2_QOS_QID_INNER) 92962306a36Sopenharmony_ci return; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci list_for_each_entry(tmp, &node->child_schq_list, list) { 93262306a36Sopenharmony_ci if (tmp->level == NIX_TXSCH_LVL_MDQ) { 93362306a36Sopenharmony_ci if (action == QOS_SMQ_FLUSH) 93462306a36Sopenharmony_ci otx2_smq_flush(pfvf, tmp->schq); 93562306a36Sopenharmony_ci else 93662306a36Sopenharmony_ci otx2_qos_enadis_sq(pfvf, tmp, node->qid); 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic void __otx2_qos_update_smq(struct otx2_nic *pfvf, 94262306a36Sopenharmony_ci struct otx2_qos_node *node, 94362306a36Sopenharmony_ci bool action) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct otx2_qos_node *tmp; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci list_for_each_entry(tmp, &node->child_list, list) { 94862306a36Sopenharmony_ci __otx2_qos_update_smq(pfvf, tmp, action); 94962306a36Sopenharmony_ci if (tmp->qid == OTX2_QOS_QID_INNER) 95062306a36Sopenharmony_ci continue; 95162306a36Sopenharmony_ci if (tmp->level == NIX_TXSCH_LVL_MDQ) { 95262306a36Sopenharmony_ci if (action == QOS_SMQ_FLUSH) 95362306a36Sopenharmony_ci otx2_smq_flush(pfvf, tmp->schq); 95462306a36Sopenharmony_ci else 95562306a36Sopenharmony_ci otx2_qos_enadis_sq(pfvf, tmp, tmp->qid); 95662306a36Sopenharmony_ci } else { 95762306a36Sopenharmony_ci otx2_qos_update_smq_schq(pfvf, tmp, action); 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cistatic void otx2_qos_update_smq(struct otx2_nic *pfvf, 96362306a36Sopenharmony_ci struct otx2_qos_node *node, 96462306a36Sopenharmony_ci bool action) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci mutex_lock(&pfvf->qos.qos_lock); 96762306a36Sopenharmony_ci __otx2_qos_update_smq(pfvf, node, action); 96862306a36Sopenharmony_ci otx2_qos_update_smq_schq(pfvf, node, action); 96962306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic int otx2_qos_push_txschq_cfg(struct otx2_nic *pfvf, 97362306a36Sopenharmony_ci struct otx2_qos_node *node, 97462306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci int ret; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci ret = otx2_qos_txschq_alloc(pfvf, cfg); 97962306a36Sopenharmony_ci if (ret) 98062306a36Sopenharmony_ci return -ENOSPC; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci ret = otx2_qos_assign_base_idx(pfvf, node); 98362306a36Sopenharmony_ci if (ret) 98462306a36Sopenharmony_ci return -ENOMEM; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (!(pfvf->netdev->flags & IFF_UP)) { 98762306a36Sopenharmony_ci otx2_qos_txschq_fill_cfg(pfvf, node, cfg); 98862306a36Sopenharmony_ci return 0; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci ret = otx2_qos_txschq_update_config(pfvf, node, cfg); 99262306a36Sopenharmony_ci if (ret) { 99362306a36Sopenharmony_ci otx2_qos_free_cfg(pfvf, cfg); 99462306a36Sopenharmony_ci return -EIO; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci otx2_qos_update_smq(pfvf, node, QOS_CFG_SQ); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci return 0; 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic int otx2_qos_update_tree(struct otx2_nic *pfvf, 100362306a36Sopenharmony_ci struct otx2_qos_node *node, 100462306a36Sopenharmony_ci struct otx2_qos_cfg *cfg) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci otx2_qos_prepare_txschq_cfg(pfvf, node->parent, cfg); 100762306a36Sopenharmony_ci return otx2_qos_push_txschq_cfg(pfvf, node->parent, cfg); 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_cistatic int otx2_qos_root_add(struct otx2_nic *pfvf, u16 htb_maj_id, u16 htb_defcls, 101162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci struct otx2_qos_cfg *new_cfg; 101462306a36Sopenharmony_ci struct otx2_qos_node *root; 101562306a36Sopenharmony_ci int err; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci netdev_dbg(pfvf->netdev, 101862306a36Sopenharmony_ci "TC_HTB_CREATE: handle=0x%x defcls=0x%x\n", 101962306a36Sopenharmony_ci htb_maj_id, htb_defcls); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci root = otx2_qos_alloc_root(pfvf); 102262306a36Sopenharmony_ci if (IS_ERR(root)) { 102362306a36Sopenharmony_ci err = PTR_ERR(root); 102462306a36Sopenharmony_ci return err; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* allocate txschq queue */ 102862306a36Sopenharmony_ci new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); 102962306a36Sopenharmony_ci if (!new_cfg) { 103062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); 103162306a36Sopenharmony_ci err = -ENOMEM; 103262306a36Sopenharmony_ci goto free_root_node; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci /* allocate htb root node */ 103562306a36Sopenharmony_ci new_cfg->schq[root->level] = 1; 103662306a36Sopenharmony_ci err = otx2_qos_txschq_alloc(pfvf, new_cfg); 103762306a36Sopenharmony_ci if (err) { 103862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Error allocating txschq"); 103962306a36Sopenharmony_ci goto free_root_node; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* Update TL1 RR PRIO */ 104362306a36Sopenharmony_ci if (root->level == NIX_TXSCH_LVL_TL1) { 104462306a36Sopenharmony_ci root->child_dwrr_prio = pfvf->hw.txschq_aggr_lvl_rr_prio; 104562306a36Sopenharmony_ci netdev_dbg(pfvf->netdev, 104662306a36Sopenharmony_ci "TL1 DWRR Priority %d\n", root->child_dwrr_prio); 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (!(pfvf->netdev->flags & IFF_UP) || 105062306a36Sopenharmony_ci root->level == NIX_TXSCH_LVL_TL1) { 105162306a36Sopenharmony_ci root->schq = new_cfg->schq_list[root->level][0]; 105262306a36Sopenharmony_ci goto out; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci /* update the txschq configuration in hw */ 105662306a36Sopenharmony_ci err = otx2_qos_txschq_update_root_cfg(pfvf, root, new_cfg); 105762306a36Sopenharmony_ci if (err) { 105862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 105962306a36Sopenharmony_ci "Error updating txschq configuration"); 106062306a36Sopenharmony_ci goto txschq_free; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ciout: 106462306a36Sopenharmony_ci WRITE_ONCE(pfvf->qos.defcls, htb_defcls); 106562306a36Sopenharmony_ci /* Pairs with smp_load_acquire() in ndo_select_queue */ 106662306a36Sopenharmony_ci smp_store_release(&pfvf->qos.maj_id, htb_maj_id); 106762306a36Sopenharmony_ci kfree(new_cfg); 106862306a36Sopenharmony_ci return 0; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_citxschq_free: 107162306a36Sopenharmony_ci otx2_qos_free_cfg(pfvf, new_cfg); 107262306a36Sopenharmony_cifree_root_node: 107362306a36Sopenharmony_ci kfree(new_cfg); 107462306a36Sopenharmony_ci otx2_qos_sw_node_delete(pfvf, root); 107562306a36Sopenharmony_ci return err; 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_cistatic int otx2_qos_root_destroy(struct otx2_nic *pfvf) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci struct otx2_qos_node *root; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci netdev_dbg(pfvf->netdev, "TC_HTB_DESTROY\n"); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci /* find root node */ 108562306a36Sopenharmony_ci root = otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID); 108662306a36Sopenharmony_ci if (!root) 108762306a36Sopenharmony_ci return -ENOENT; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* free the hw mappings */ 109062306a36Sopenharmony_ci otx2_qos_destroy_node(pfvf, root); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci return 0; 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic int otx2_qos_validate_quantum(struct otx2_nic *pfvf, u32 quantum) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci u32 rr_weight = otx2_qos_quantum_to_dwrr_weight(pfvf, quantum); 109862306a36Sopenharmony_ci int err = 0; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci /* Max Round robin weight supported by octeontx2 and CN10K 110162306a36Sopenharmony_ci * is different. Validate accordingly 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ci if (is_dev_otx2(pfvf->pdev)) 110462306a36Sopenharmony_ci err = (rr_weight > OTX2_MAX_RR_QUANTUM) ? -EINVAL : 0; 110562306a36Sopenharmony_ci else if (rr_weight > CN10K_MAX_RR_WEIGHT) 110662306a36Sopenharmony_ci err = -EINVAL; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci return err; 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic int otx2_qos_validate_dwrr_cfg(struct otx2_qos_node *parent, 111262306a36Sopenharmony_ci struct netlink_ext_ack *extack, 111362306a36Sopenharmony_ci struct otx2_nic *pfvf, 111462306a36Sopenharmony_ci u64 prio, u64 quantum) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci int err; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci err = otx2_qos_validate_quantum(pfvf, quantum); 111962306a36Sopenharmony_ci if (err) { 112062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported quantum value"); 112162306a36Sopenharmony_ci return err; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci if (parent->child_dwrr_prio == OTX2_QOS_DEFAULT_PRIO) { 112562306a36Sopenharmony_ci parent->child_dwrr_prio = prio; 112662306a36Sopenharmony_ci } else if (prio != parent->child_dwrr_prio) { 112762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Only one DWRR group is allowed"); 112862306a36Sopenharmony_ci return -EOPNOTSUPP; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci return 0; 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic int otx2_qos_validate_configuration(struct otx2_qos_node *parent, 113562306a36Sopenharmony_ci struct netlink_ext_ack *extack, 113662306a36Sopenharmony_ci struct otx2_nic *pfvf, 113762306a36Sopenharmony_ci u64 prio, bool static_cfg) 113862306a36Sopenharmony_ci{ 113962306a36Sopenharmony_ci if (prio == parent->child_dwrr_prio && static_cfg) { 114062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "DWRR child group with same priority exists"); 114162306a36Sopenharmony_ci return -EEXIST; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci if (static_cfg && test_bit(prio, parent->prio_bmap)) { 114562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 114662306a36Sopenharmony_ci "Static priority child with same priority exists"); 114762306a36Sopenharmony_ci return -EEXIST; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci return 0; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic void otx2_reset_dwrr_prio(struct otx2_qos_node *parent, u64 prio) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci /* For PF, root node dwrr priority is static */ 115662306a36Sopenharmony_ci if (parent->level == NIX_TXSCH_LVL_TL1) 115762306a36Sopenharmony_ci return; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (parent->child_dwrr_prio != OTX2_QOS_DEFAULT_PRIO) { 116062306a36Sopenharmony_ci parent->child_dwrr_prio = OTX2_QOS_DEFAULT_PRIO; 116162306a36Sopenharmony_ci clear_bit(prio, parent->prio_bmap); 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic bool is_qos_node_dwrr(struct otx2_qos_node *parent, 116662306a36Sopenharmony_ci struct otx2_nic *pfvf, 116762306a36Sopenharmony_ci u64 prio) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci struct otx2_qos_node *node; 117062306a36Sopenharmony_ci bool ret = false; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (parent->child_dwrr_prio == prio) 117362306a36Sopenharmony_ci return true; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci mutex_lock(&pfvf->qos.qos_lock); 117662306a36Sopenharmony_ci list_for_each_entry(node, &parent->child_list, list) { 117762306a36Sopenharmony_ci if (prio == node->prio) { 117862306a36Sopenharmony_ci if (parent->child_dwrr_prio != OTX2_QOS_DEFAULT_PRIO && 117962306a36Sopenharmony_ci parent->child_dwrr_prio != prio) 118062306a36Sopenharmony_ci continue; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (otx2_qos_validate_quantum(pfvf, node->quantum)) { 118362306a36Sopenharmony_ci netdev_err(pfvf->netdev, 118462306a36Sopenharmony_ci "Unsupported quantum value for existing classid=0x%x quantum=%d prio=%d", 118562306a36Sopenharmony_ci node->classid, node->quantum, 118662306a36Sopenharmony_ci node->prio); 118762306a36Sopenharmony_ci break; 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci /* mark old node as dwrr */ 119062306a36Sopenharmony_ci node->is_static = false; 119162306a36Sopenharmony_ci parent->child_dwrr_cnt++; 119262306a36Sopenharmony_ci parent->child_static_cnt--; 119362306a36Sopenharmony_ci ret = true; 119462306a36Sopenharmony_ci break; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci mutex_unlock(&pfvf->qos.qos_lock); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci return ret; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic int otx2_qos_leaf_alloc_queue(struct otx2_nic *pfvf, u16 classid, 120362306a36Sopenharmony_ci u32 parent_classid, u64 rate, u64 ceil, 120462306a36Sopenharmony_ci u64 prio, u32 quantum, 120562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 120662306a36Sopenharmony_ci{ 120762306a36Sopenharmony_ci struct otx2_qos_cfg *old_cfg, *new_cfg; 120862306a36Sopenharmony_ci struct otx2_qos_node *node, *parent; 120962306a36Sopenharmony_ci int qid, ret, err; 121062306a36Sopenharmony_ci bool static_cfg; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci netdev_dbg(pfvf->netdev, 121362306a36Sopenharmony_ci "TC_HTB_LEAF_ALLOC_QUEUE: classid=0x%x parent_classid=0x%x rate=%lld ceil=%lld prio=%lld quantum=%d\n", 121462306a36Sopenharmony_ci classid, parent_classid, rate, ceil, prio, quantum); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci if (prio > OTX2_QOS_MAX_PRIO) { 121762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Valid priority range 0 to 7"); 121862306a36Sopenharmony_ci ret = -EOPNOTSUPP; 121962306a36Sopenharmony_ci goto out; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (!quantum || quantum > INT_MAX) { 122362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid quantum, range 1 - 2147483647 bytes"); 122462306a36Sopenharmony_ci ret = -EOPNOTSUPP; 122562306a36Sopenharmony_ci goto out; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci /* get parent node */ 122962306a36Sopenharmony_ci parent = otx2_sw_node_find(pfvf, parent_classid); 123062306a36Sopenharmony_ci if (!parent) { 123162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "parent node not found"); 123262306a36Sopenharmony_ci ret = -ENOENT; 123362306a36Sopenharmony_ci goto out; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci if (parent->level == NIX_TXSCH_LVL_MDQ) { 123662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "HTB qos max levels reached"); 123762306a36Sopenharmony_ci ret = -EOPNOTSUPP; 123862306a36Sopenharmony_ci goto out; 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci static_cfg = !is_qos_node_dwrr(parent, pfvf, prio); 124262306a36Sopenharmony_ci ret = otx2_qos_validate_configuration(parent, extack, pfvf, prio, 124362306a36Sopenharmony_ci static_cfg); 124462306a36Sopenharmony_ci if (ret) 124562306a36Sopenharmony_ci goto out; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci if (!static_cfg) { 124862306a36Sopenharmony_ci ret = otx2_qos_validate_dwrr_cfg(parent, extack, pfvf, prio, 124962306a36Sopenharmony_ci quantum); 125062306a36Sopenharmony_ci if (ret) 125162306a36Sopenharmony_ci goto out; 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci if (static_cfg) 125562306a36Sopenharmony_ci parent->child_static_cnt++; 125662306a36Sopenharmony_ci else 125762306a36Sopenharmony_ci parent->child_dwrr_cnt++; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci set_bit(prio, parent->prio_bmap); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* read current txschq configuration */ 126262306a36Sopenharmony_ci old_cfg = kzalloc(sizeof(*old_cfg), GFP_KERNEL); 126362306a36Sopenharmony_ci if (!old_cfg) { 126462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); 126562306a36Sopenharmony_ci ret = -ENOMEM; 126662306a36Sopenharmony_ci goto reset_prio; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci otx2_qos_read_txschq_cfg(pfvf, parent, old_cfg); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci /* allocate a new sq */ 127162306a36Sopenharmony_ci qid = otx2_qos_get_qid(pfvf); 127262306a36Sopenharmony_ci if (qid < 0) { 127362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Reached max supported QOS SQ's"); 127462306a36Sopenharmony_ci ret = -ENOMEM; 127562306a36Sopenharmony_ci goto free_old_cfg; 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci /* Actual SQ mapping will be updated after SMQ alloc */ 127962306a36Sopenharmony_ci pfvf->qos.qid_to_sqmap[qid] = OTX2_QOS_INVALID_SQ; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci /* allocate and initialize a new child node */ 128262306a36Sopenharmony_ci node = otx2_qos_sw_create_leaf_node(pfvf, parent, classid, prio, rate, 128362306a36Sopenharmony_ci ceil, quantum, qid, static_cfg); 128462306a36Sopenharmony_ci if (IS_ERR(node)) { 128562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unable to allocate leaf node"); 128662306a36Sopenharmony_ci ret = PTR_ERR(node); 128762306a36Sopenharmony_ci goto free_old_cfg; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci /* push new txschq config to hw */ 129162306a36Sopenharmony_ci new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); 129262306a36Sopenharmony_ci if (!new_cfg) { 129362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); 129462306a36Sopenharmony_ci ret = -ENOMEM; 129562306a36Sopenharmony_ci goto free_node; 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci ret = otx2_qos_update_tree(pfvf, node, new_cfg); 129862306a36Sopenharmony_ci if (ret) { 129962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "HTB HW configuration error"); 130062306a36Sopenharmony_ci kfree(new_cfg); 130162306a36Sopenharmony_ci otx2_qos_sw_node_delete(pfvf, node); 130262306a36Sopenharmony_ci /* restore the old qos tree */ 130362306a36Sopenharmony_ci err = otx2_qos_txschq_update_config(pfvf, parent, old_cfg); 130462306a36Sopenharmony_ci if (err) { 130562306a36Sopenharmony_ci netdev_err(pfvf->netdev, 130662306a36Sopenharmony_ci "Failed to restore txcshq configuration"); 130762306a36Sopenharmony_ci goto free_old_cfg; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci otx2_qos_update_smq(pfvf, parent, QOS_CFG_SQ); 131162306a36Sopenharmony_ci goto free_old_cfg; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci /* update tx_real_queues */ 131562306a36Sopenharmony_ci otx2_qos_update_tx_netdev_queues(pfvf); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci /* free new txschq config */ 131862306a36Sopenharmony_ci kfree(new_cfg); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci /* free old txschq config */ 132162306a36Sopenharmony_ci otx2_qos_free_cfg(pfvf, old_cfg); 132262306a36Sopenharmony_ci kfree(old_cfg); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci return pfvf->hw.tx_queues + qid; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_cifree_node: 132762306a36Sopenharmony_ci otx2_qos_sw_node_delete(pfvf, node); 132862306a36Sopenharmony_cifree_old_cfg: 132962306a36Sopenharmony_ci kfree(old_cfg); 133062306a36Sopenharmony_cireset_prio: 133162306a36Sopenharmony_ci if (static_cfg) 133262306a36Sopenharmony_ci parent->child_static_cnt--; 133362306a36Sopenharmony_ci else 133462306a36Sopenharmony_ci parent->child_dwrr_cnt--; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci clear_bit(prio, parent->prio_bmap); 133762306a36Sopenharmony_ciout: 133862306a36Sopenharmony_ci return ret; 133962306a36Sopenharmony_ci} 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_cistatic int otx2_qos_leaf_to_inner(struct otx2_nic *pfvf, u16 classid, 134262306a36Sopenharmony_ci u16 child_classid, u64 rate, u64 ceil, u64 prio, 134362306a36Sopenharmony_ci u32 quantum, struct netlink_ext_ack *extack) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci struct otx2_qos_cfg *old_cfg, *new_cfg; 134662306a36Sopenharmony_ci struct otx2_qos_node *node, *child; 134762306a36Sopenharmony_ci bool static_cfg; 134862306a36Sopenharmony_ci int ret, err; 134962306a36Sopenharmony_ci u16 qid; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci netdev_dbg(pfvf->netdev, 135262306a36Sopenharmony_ci "TC_HTB_LEAF_TO_INNER classid %04x, child %04x, rate %llu, ceil %llu\n", 135362306a36Sopenharmony_ci classid, child_classid, rate, ceil); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (prio > OTX2_QOS_MAX_PRIO) { 135662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Valid priority range 0 to 7"); 135762306a36Sopenharmony_ci ret = -EOPNOTSUPP; 135862306a36Sopenharmony_ci goto out; 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci if (!quantum || quantum > INT_MAX) { 136262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid quantum, range 1 - 2147483647 bytes"); 136362306a36Sopenharmony_ci ret = -EOPNOTSUPP; 136462306a36Sopenharmony_ci goto out; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci /* find node related to classid */ 136862306a36Sopenharmony_ci node = otx2_sw_node_find(pfvf, classid); 136962306a36Sopenharmony_ci if (!node) { 137062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "HTB node not found"); 137162306a36Sopenharmony_ci ret = -ENOENT; 137262306a36Sopenharmony_ci goto out; 137362306a36Sopenharmony_ci } 137462306a36Sopenharmony_ci /* check max qos txschq level */ 137562306a36Sopenharmony_ci if (node->level == NIX_TXSCH_LVL_MDQ) { 137662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "HTB qos level not supported"); 137762306a36Sopenharmony_ci ret = -EOPNOTSUPP; 137862306a36Sopenharmony_ci goto out; 137962306a36Sopenharmony_ci } 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci static_cfg = !is_qos_node_dwrr(node, pfvf, prio); 138262306a36Sopenharmony_ci if (!static_cfg) { 138362306a36Sopenharmony_ci ret = otx2_qos_validate_dwrr_cfg(node, extack, pfvf, prio, 138462306a36Sopenharmony_ci quantum); 138562306a36Sopenharmony_ci if (ret) 138662306a36Sopenharmony_ci goto out; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci if (static_cfg) 139062306a36Sopenharmony_ci node->child_static_cnt++; 139162306a36Sopenharmony_ci else 139262306a36Sopenharmony_ci node->child_dwrr_cnt++; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci set_bit(prio, node->prio_bmap); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci /* store the qid to assign to leaf node */ 139762306a36Sopenharmony_ci qid = node->qid; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci /* read current txschq configuration */ 140062306a36Sopenharmony_ci old_cfg = kzalloc(sizeof(*old_cfg), GFP_KERNEL); 140162306a36Sopenharmony_ci if (!old_cfg) { 140262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); 140362306a36Sopenharmony_ci ret = -ENOMEM; 140462306a36Sopenharmony_ci goto reset_prio; 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci otx2_qos_read_txschq_cfg(pfvf, node, old_cfg); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci /* delete the txschq nodes allocated for this node */ 140962306a36Sopenharmony_ci otx2_qos_free_sw_node_schq(pfvf, node); 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci /* mark this node as htb inner node */ 141262306a36Sopenharmony_ci WRITE_ONCE(node->qid, OTX2_QOS_QID_INNER); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci /* allocate and initialize a new child node */ 141562306a36Sopenharmony_ci child = otx2_qos_sw_create_leaf_node(pfvf, node, child_classid, 141662306a36Sopenharmony_ci prio, rate, ceil, quantum, 141762306a36Sopenharmony_ci qid, static_cfg); 141862306a36Sopenharmony_ci if (IS_ERR(child)) { 141962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unable to allocate leaf node"); 142062306a36Sopenharmony_ci ret = PTR_ERR(child); 142162306a36Sopenharmony_ci goto free_old_cfg; 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci /* push new txschq config to hw */ 142562306a36Sopenharmony_ci new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); 142662306a36Sopenharmony_ci if (!new_cfg) { 142762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); 142862306a36Sopenharmony_ci ret = -ENOMEM; 142962306a36Sopenharmony_ci goto free_node; 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci ret = otx2_qos_update_tree(pfvf, child, new_cfg); 143262306a36Sopenharmony_ci if (ret) { 143362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "HTB HW configuration error"); 143462306a36Sopenharmony_ci kfree(new_cfg); 143562306a36Sopenharmony_ci otx2_qos_sw_node_delete(pfvf, child); 143662306a36Sopenharmony_ci /* restore the old qos tree */ 143762306a36Sopenharmony_ci WRITE_ONCE(node->qid, qid); 143862306a36Sopenharmony_ci err = otx2_qos_alloc_txschq_node(pfvf, node); 143962306a36Sopenharmony_ci if (err) { 144062306a36Sopenharmony_ci netdev_err(pfvf->netdev, 144162306a36Sopenharmony_ci "Failed to restore old leaf node"); 144262306a36Sopenharmony_ci goto free_old_cfg; 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci err = otx2_qos_txschq_update_config(pfvf, node, old_cfg); 144562306a36Sopenharmony_ci if (err) { 144662306a36Sopenharmony_ci netdev_err(pfvf->netdev, 144762306a36Sopenharmony_ci "Failed to restore txcshq configuration"); 144862306a36Sopenharmony_ci goto free_old_cfg; 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci otx2_qos_update_smq(pfvf, node, QOS_CFG_SQ); 145162306a36Sopenharmony_ci goto free_old_cfg; 145262306a36Sopenharmony_ci } 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci /* free new txschq config */ 145562306a36Sopenharmony_ci kfree(new_cfg); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci /* free old txschq config */ 145862306a36Sopenharmony_ci otx2_qos_free_cfg(pfvf, old_cfg); 145962306a36Sopenharmony_ci kfree(old_cfg); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci return 0; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_cifree_node: 146462306a36Sopenharmony_ci otx2_qos_sw_node_delete(pfvf, child); 146562306a36Sopenharmony_cifree_old_cfg: 146662306a36Sopenharmony_ci kfree(old_cfg); 146762306a36Sopenharmony_cireset_prio: 146862306a36Sopenharmony_ci if (static_cfg) 146962306a36Sopenharmony_ci node->child_static_cnt--; 147062306a36Sopenharmony_ci else 147162306a36Sopenharmony_ci node->child_dwrr_cnt--; 147262306a36Sopenharmony_ci clear_bit(prio, node->prio_bmap); 147362306a36Sopenharmony_ciout: 147462306a36Sopenharmony_ci return ret; 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic int otx2_qos_leaf_del(struct otx2_nic *pfvf, u16 *classid, 147862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 147962306a36Sopenharmony_ci{ 148062306a36Sopenharmony_ci struct otx2_qos_node *node, *parent; 148162306a36Sopenharmony_ci int dwrr_del_node = false; 148262306a36Sopenharmony_ci u64 prio; 148362306a36Sopenharmony_ci u16 qid; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci netdev_dbg(pfvf->netdev, "TC_HTB_LEAF_DEL classid %04x\n", *classid); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci /* find node related to classid */ 148862306a36Sopenharmony_ci node = otx2_sw_node_find(pfvf, *classid); 148962306a36Sopenharmony_ci if (!node) { 149062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "HTB node not found"); 149162306a36Sopenharmony_ci return -ENOENT; 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci parent = node->parent; 149462306a36Sopenharmony_ci prio = node->prio; 149562306a36Sopenharmony_ci qid = node->qid; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci if (!node->is_static) 149862306a36Sopenharmony_ci dwrr_del_node = true; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci otx2_qos_disable_sq(pfvf, node->qid); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci otx2_qos_destroy_node(pfvf, node); 150362306a36Sopenharmony_ci pfvf->qos.qid_to_sqmap[qid] = OTX2_QOS_INVALID_SQ; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci if (dwrr_del_node) { 150662306a36Sopenharmony_ci parent->child_dwrr_cnt--; 150762306a36Sopenharmony_ci } else { 150862306a36Sopenharmony_ci parent->child_static_cnt--; 150962306a36Sopenharmony_ci clear_bit(prio, parent->prio_bmap); 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci /* Reset DWRR priority if all dwrr nodes are deleted */ 151362306a36Sopenharmony_ci if (!parent->child_dwrr_cnt) 151462306a36Sopenharmony_ci otx2_reset_dwrr_prio(parent, prio); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci if (!parent->child_static_cnt) 151762306a36Sopenharmony_ci parent->max_static_prio = 0; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci return 0; 152062306a36Sopenharmony_ci} 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_cistatic int otx2_qos_leaf_del_last(struct otx2_nic *pfvf, u16 classid, bool force, 152362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 152462306a36Sopenharmony_ci{ 152562306a36Sopenharmony_ci struct otx2_qos_node *node, *parent; 152662306a36Sopenharmony_ci struct otx2_qos_cfg *new_cfg; 152762306a36Sopenharmony_ci int dwrr_del_node = false; 152862306a36Sopenharmony_ci u64 prio; 152962306a36Sopenharmony_ci int err; 153062306a36Sopenharmony_ci u16 qid; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci netdev_dbg(pfvf->netdev, 153362306a36Sopenharmony_ci "TC_HTB_LEAF_DEL_LAST classid %04x\n", classid); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci /* find node related to classid */ 153662306a36Sopenharmony_ci node = otx2_sw_node_find(pfvf, classid); 153762306a36Sopenharmony_ci if (!node) { 153862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "HTB node not found"); 153962306a36Sopenharmony_ci return -ENOENT; 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci /* save qid for use by parent */ 154362306a36Sopenharmony_ci qid = node->qid; 154462306a36Sopenharmony_ci prio = node->prio; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci parent = otx2_sw_node_find(pfvf, node->parent->classid); 154762306a36Sopenharmony_ci if (!parent) { 154862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "parent node not found"); 154962306a36Sopenharmony_ci return -ENOENT; 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci if (!node->is_static) 155362306a36Sopenharmony_ci dwrr_del_node = true; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci /* destroy the leaf node */ 155662306a36Sopenharmony_ci otx2_qos_destroy_node(pfvf, node); 155762306a36Sopenharmony_ci pfvf->qos.qid_to_sqmap[qid] = OTX2_QOS_INVALID_SQ; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (dwrr_del_node) { 156062306a36Sopenharmony_ci parent->child_dwrr_cnt--; 156162306a36Sopenharmony_ci } else { 156262306a36Sopenharmony_ci parent->child_static_cnt--; 156362306a36Sopenharmony_ci clear_bit(prio, parent->prio_bmap); 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci /* Reset DWRR priority if all dwrr nodes are deleted */ 156762306a36Sopenharmony_ci if (!parent->child_dwrr_cnt) 156862306a36Sopenharmony_ci otx2_reset_dwrr_prio(parent, prio); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci if (!parent->child_static_cnt) 157162306a36Sopenharmony_ci parent->max_static_prio = 0; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci /* create downstream txschq entries to parent */ 157462306a36Sopenharmony_ci err = otx2_qos_alloc_txschq_node(pfvf, parent); 157562306a36Sopenharmony_ci if (err) { 157662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "HTB failed to create txsch configuration"); 157762306a36Sopenharmony_ci return err; 157862306a36Sopenharmony_ci } 157962306a36Sopenharmony_ci WRITE_ONCE(parent->qid, qid); 158062306a36Sopenharmony_ci __set_bit(qid, pfvf->qos.qos_sq_bmap); 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci /* push new txschq config to hw */ 158362306a36Sopenharmony_ci new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); 158462306a36Sopenharmony_ci if (!new_cfg) { 158562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); 158662306a36Sopenharmony_ci return -ENOMEM; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci /* fill txschq cfg and push txschq cfg to hw */ 158962306a36Sopenharmony_ci otx2_qos_fill_cfg_schq(parent, new_cfg); 159062306a36Sopenharmony_ci err = otx2_qos_push_txschq_cfg(pfvf, parent, new_cfg); 159162306a36Sopenharmony_ci if (err) { 159262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "HTB HW configuration error"); 159362306a36Sopenharmony_ci kfree(new_cfg); 159462306a36Sopenharmony_ci return err; 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci kfree(new_cfg); 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci /* update tx_real_queues */ 159962306a36Sopenharmony_ci otx2_qos_update_tx_netdev_queues(pfvf); 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci return 0; 160262306a36Sopenharmony_ci} 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_civoid otx2_clean_qos_queues(struct otx2_nic *pfvf) 160562306a36Sopenharmony_ci{ 160662306a36Sopenharmony_ci struct otx2_qos_node *root; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci root = otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID); 160962306a36Sopenharmony_ci if (!root) 161062306a36Sopenharmony_ci return; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci otx2_qos_update_smq(pfvf, root, QOS_SMQ_FLUSH); 161362306a36Sopenharmony_ci} 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_civoid otx2_qos_config_txschq(struct otx2_nic *pfvf) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci struct otx2_qos_node *root; 161862306a36Sopenharmony_ci int err; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci root = otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID); 162162306a36Sopenharmony_ci if (!root) 162262306a36Sopenharmony_ci return; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci if (root->level != NIX_TXSCH_LVL_TL1) { 162562306a36Sopenharmony_ci err = otx2_qos_txschq_config(pfvf, root); 162662306a36Sopenharmony_ci if (err) { 162762306a36Sopenharmony_ci netdev_err(pfvf->netdev, "Error update txschq configuration\n"); 162862306a36Sopenharmony_ci goto root_destroy; 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci err = otx2_qos_txschq_push_cfg_tl(pfvf, root, NULL); 163362306a36Sopenharmony_ci if (err) { 163462306a36Sopenharmony_ci netdev_err(pfvf->netdev, "Error update txschq configuration\n"); 163562306a36Sopenharmony_ci goto root_destroy; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci otx2_qos_update_smq(pfvf, root, QOS_CFG_SQ); 163962306a36Sopenharmony_ci return; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ciroot_destroy: 164262306a36Sopenharmony_ci netdev_err(pfvf->netdev, "Failed to update Scheduler/Shaping config in Hardware\n"); 164362306a36Sopenharmony_ci /* Free resources allocated */ 164462306a36Sopenharmony_ci otx2_qos_root_destroy(pfvf); 164562306a36Sopenharmony_ci} 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ciint otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb) 164862306a36Sopenharmony_ci{ 164962306a36Sopenharmony_ci struct otx2_nic *pfvf = netdev_priv(ndev); 165062306a36Sopenharmony_ci int res; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci switch (htb->command) { 165362306a36Sopenharmony_ci case TC_HTB_CREATE: 165462306a36Sopenharmony_ci return otx2_qos_root_add(pfvf, htb->parent_classid, 165562306a36Sopenharmony_ci htb->classid, htb->extack); 165662306a36Sopenharmony_ci case TC_HTB_DESTROY: 165762306a36Sopenharmony_ci return otx2_qos_root_destroy(pfvf); 165862306a36Sopenharmony_ci case TC_HTB_LEAF_ALLOC_QUEUE: 165962306a36Sopenharmony_ci res = otx2_qos_leaf_alloc_queue(pfvf, htb->classid, 166062306a36Sopenharmony_ci htb->parent_classid, 166162306a36Sopenharmony_ci htb->rate, htb->ceil, 166262306a36Sopenharmony_ci htb->prio, htb->quantum, 166362306a36Sopenharmony_ci htb->extack); 166462306a36Sopenharmony_ci if (res < 0) 166562306a36Sopenharmony_ci return res; 166662306a36Sopenharmony_ci htb->qid = res; 166762306a36Sopenharmony_ci return 0; 166862306a36Sopenharmony_ci case TC_HTB_LEAF_TO_INNER: 166962306a36Sopenharmony_ci return otx2_qos_leaf_to_inner(pfvf, htb->parent_classid, 167062306a36Sopenharmony_ci htb->classid, htb->rate, 167162306a36Sopenharmony_ci htb->ceil, htb->prio, 167262306a36Sopenharmony_ci htb->quantum, htb->extack); 167362306a36Sopenharmony_ci case TC_HTB_LEAF_DEL: 167462306a36Sopenharmony_ci return otx2_qos_leaf_del(pfvf, &htb->classid, htb->extack); 167562306a36Sopenharmony_ci case TC_HTB_LEAF_DEL_LAST: 167662306a36Sopenharmony_ci case TC_HTB_LEAF_DEL_LAST_FORCE: 167762306a36Sopenharmony_ci return otx2_qos_leaf_del_last(pfvf, htb->classid, 167862306a36Sopenharmony_ci htb->command == TC_HTB_LEAF_DEL_LAST_FORCE, 167962306a36Sopenharmony_ci htb->extack); 168062306a36Sopenharmony_ci case TC_HTB_LEAF_QUERY_QUEUE: 168162306a36Sopenharmony_ci res = otx2_get_txq_by_classid(pfvf, htb->classid); 168262306a36Sopenharmony_ci htb->qid = res; 168362306a36Sopenharmony_ci return 0; 168462306a36Sopenharmony_ci case TC_HTB_NODE_MODIFY: 168562306a36Sopenharmony_ci fallthrough; 168662306a36Sopenharmony_ci default: 168762306a36Sopenharmony_ci return -EOPNOTSUPP; 168862306a36Sopenharmony_ci } 168962306a36Sopenharmony_ci} 1690