// SPDX-License-Identifier: GPL-2.0 /* Marvell RVU Physical Function ethernet driver * * Copyright (C) 2023 Marvell. * */ #include #include #include "cn10k.h" #include "otx2_reg.h" #include "otx2_common.h" #include "otx2_txrx.h" #include "otx2_struct.h" #define OTX2_QOS_MAX_LEAF_NODES 16 static void otx2_qos_aura_pool_free(struct otx2_nic *pfvf, int pool_id) { struct otx2_pool *pool; if (!pfvf->qset.pool) return; pool = &pfvf->qset.pool[pool_id]; qmem_free(pfvf->dev, pool->stack); qmem_free(pfvf->dev, pool->fc_addr); pool->stack = NULL; pool->fc_addr = NULL; } static int otx2_qos_sq_aura_pool_init(struct otx2_nic *pfvf, int qidx) { struct otx2_qset *qset = &pfvf->qset; int pool_id, stack_pages, num_sqbs; struct otx2_hw *hw = &pfvf->hw; struct otx2_snd_queue *sq; struct otx2_pool *pool; dma_addr_t bufptr; int err, ptr; u64 iova, pa; /* Calculate number of SQBs needed. * * For a 128byte SQE, and 4K size SQB, 31 SQEs will fit in one SQB. * Last SQE is used for pointing to next SQB. */ num_sqbs = (hw->sqb_size / 128) - 1; num_sqbs = (qset->sqe_cnt + num_sqbs) / num_sqbs; /* Get no of stack pages needed */ stack_pages = (num_sqbs + hw->stack_pg_ptrs - 1) / hw->stack_pg_ptrs; pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx); pool = &pfvf->qset.pool[pool_id]; /* Initialize aura context */ err = otx2_aura_init(pfvf, pool_id, pool_id, num_sqbs); if (err) return err; /* Initialize pool context */ err = otx2_pool_init(pfvf, pool_id, stack_pages, num_sqbs, hw->sqb_size, AURA_NIX_SQ); if (err) goto aura_free; /* Flush accumulated messages */ err = otx2_sync_mbox_msg(&pfvf->mbox); if (err) goto pool_free; /* Allocate pointers and free them to aura/pool */ sq = &qset->sq[qidx]; sq->sqb_count = 0; sq->sqb_ptrs = kcalloc(num_sqbs, sizeof(*sq->sqb_ptrs), GFP_KERNEL); if (!sq->sqb_ptrs) { err = -ENOMEM; goto pool_free; } for (ptr = 0; ptr < num_sqbs; ptr++) { err = otx2_alloc_rbuf(pfvf, pool, &bufptr); if (err) goto sqb_free; pfvf->hw_ops->aura_freeptr(pfvf, pool_id, bufptr); sq->sqb_ptrs[sq->sqb_count++] = (u64)bufptr; } return 0; sqb_free: while (ptr--) { if (!sq->sqb_ptrs[ptr]) continue; iova = sq->sqb_ptrs[ptr]; pa = otx2_iova_to_phys(pfvf->iommu_domain, iova); dma_unmap_page_attrs(pfvf->dev, iova, hw->sqb_size, DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); put_page(virt_to_page(phys_to_virt(pa))); otx2_aura_allocptr(pfvf, pool_id); } sq->sqb_count = 0; kfree(sq->sqb_ptrs); pool_free: qmem_free(pfvf->dev, pool->stack); aura_free: qmem_free(pfvf->dev, pool->fc_addr); otx2_mbox_reset(&pfvf->mbox.mbox, 0); return err; } static void otx2_qos_sq_free_sqbs(struct otx2_nic *pfvf, int qidx) { struct otx2_qset *qset = &pfvf->qset; struct otx2_hw *hw = &pfvf->hw; struct otx2_snd_queue *sq; u64 iova, pa; int sqb; sq = &qset->sq[qidx]; if (!sq->sqb_ptrs) return; for (sqb = 0; sqb < sq->sqb_count; sqb++) { if (!sq->sqb_ptrs[sqb]) continue; iova = sq->sqb_ptrs[sqb]; pa = otx2_iova_to_phys(pfvf->iommu_domain, iova); dma_unmap_page_attrs(pfvf->dev, iova, hw->sqb_size, DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); put_page(virt_to_page(phys_to_virt(pa))); } sq->sqb_count = 0; sq = &qset->sq[qidx]; qmem_free(pfvf->dev, sq->sqe); qmem_free(pfvf->dev, sq->tso_hdrs); kfree(sq->sg); kfree(sq->sqb_ptrs); qmem_free(pfvf->dev, sq->timestamps); memset((void *)sq, 0, sizeof(*sq)); } /* send queue id */ static void otx2_qos_sqb_flush(struct otx2_nic *pfvf, int qidx) { int sqe_tail, sqe_head; u64 incr, *ptr, val; ptr = (__force u64 *)otx2_get_regaddr(pfvf, NIX_LF_SQ_OP_STATUS); incr = (u64)qidx << 32; val = otx2_atomic64_add(incr, ptr); sqe_head = (val >> 20) & 0x3F; sqe_tail = (val >> 28) & 0x3F; if (sqe_head != sqe_tail) usleep_range(50, 60); } static int otx2_qos_ctx_disable(struct otx2_nic *pfvf, u16 qidx, int aura_id) { struct nix_cn10k_aq_enq_req *cn10k_sq_aq; struct npa_aq_enq_req *aura_aq; struct npa_aq_enq_req *pool_aq; struct nix_aq_enq_req *sq_aq; if (test_bit(CN10K_LMTST, &pfvf->hw.cap_flag)) { cn10k_sq_aq = otx2_mbox_alloc_msg_nix_cn10k_aq_enq(&pfvf->mbox); if (!cn10k_sq_aq) return -ENOMEM; cn10k_sq_aq->qidx = qidx; cn10k_sq_aq->sq.ena = 0; cn10k_sq_aq->sq_mask.ena = 1; cn10k_sq_aq->ctype = NIX_AQ_CTYPE_SQ; cn10k_sq_aq->op = NIX_AQ_INSTOP_WRITE; } else { sq_aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); if (!sq_aq) return -ENOMEM; sq_aq->qidx = qidx; sq_aq->sq.ena = 0; sq_aq->sq_mask.ena = 1; sq_aq->ctype = NIX_AQ_CTYPE_SQ; sq_aq->op = NIX_AQ_INSTOP_WRITE; } aura_aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); if (!aura_aq) { otx2_mbox_reset(&pfvf->mbox.mbox, 0); return -ENOMEM; } aura_aq->aura_id = aura_id; aura_aq->aura.ena = 0; aura_aq->aura_mask.ena = 1; aura_aq->ctype = NPA_AQ_CTYPE_AURA; aura_aq->op = NPA_AQ_INSTOP_WRITE; pool_aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); if (!pool_aq) { otx2_mbox_reset(&pfvf->mbox.mbox, 0); return -ENOMEM; } pool_aq->aura_id = aura_id; pool_aq->pool.ena = 0; pool_aq->pool_mask.ena = 1; pool_aq->ctype = NPA_AQ_CTYPE_POOL; pool_aq->op = NPA_AQ_INSTOP_WRITE; return otx2_sync_mbox_msg(&pfvf->mbox); } int otx2_qos_get_qid(struct otx2_nic *pfvf) { int qidx; qidx = find_first_zero_bit(pfvf->qos.qos_sq_bmap, pfvf->hw.tc_tx_queues); return qidx == pfvf->hw.tc_tx_queues ? -ENOSPC : qidx; } void otx2_qos_free_qid(struct otx2_nic *pfvf, int qidx) { clear_bit(qidx, pfvf->qos.qos_sq_bmap); } int otx2_qos_enable_sq(struct otx2_nic *pfvf, int qidx) { struct otx2_hw *hw = &pfvf->hw; int pool_id, sq_idx, err; if (pfvf->flags & OTX2_FLAG_INTF_DOWN) return -EPERM; sq_idx = hw->non_qos_queues + qidx; mutex_lock(&pfvf->mbox.lock); err = otx2_qos_sq_aura_pool_init(pfvf, sq_idx); if (err) goto out; pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, sq_idx); err = otx2_sq_init(pfvf, sq_idx, pool_id); if (err) goto out; out: mutex_unlock(&pfvf->mbox.lock); return err; } void otx2_qos_disable_sq(struct otx2_nic *pfvf, int qidx) { struct otx2_qset *qset = &pfvf->qset; struct otx2_hw *hw = &pfvf->hw; struct otx2_snd_queue *sq; struct otx2_cq_queue *cq; int pool_id, sq_idx; sq_idx = hw->non_qos_queues + qidx; /* If the DOWN flag is set SQs are already freed */ if (pfvf->flags & OTX2_FLAG_INTF_DOWN) return; sq = &pfvf->qset.sq[sq_idx]; if (!sq->sqb_ptrs) return; if (sq_idx < hw->non_qos_queues || sq_idx >= otx2_get_total_tx_queues(pfvf)) { netdev_err(pfvf->netdev, "Send Queue is not a QoS queue\n"); return; } cq = &qset->cq[pfvf->hw.rx_queues + sq_idx]; pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, sq_idx); otx2_qos_sqb_flush(pfvf, sq_idx); otx2_smq_flush(pfvf, otx2_get_smq_idx(pfvf, sq_idx)); otx2_cleanup_tx_cqes(pfvf, cq); mutex_lock(&pfvf->mbox.lock); otx2_qos_ctx_disable(pfvf, sq_idx, pool_id); mutex_unlock(&pfvf->mbox.lock); otx2_qos_sq_free_sqbs(pfvf, sq_idx); otx2_qos_aura_pool_free(pfvf, pool_id); }