Merge branch 'octeontx2-pf-HTB'

Hariprasad Kelam says:

====================
octeontx2-pf: HTB offload support

octeontx2 silicon and CN10K transmit interface consists of five
transmit levels starting from MDQ, TL4 to TL1. Once packets are
submitted to MDQ, hardware picks all active MDQs using strict
priority, and MDQs having the same priority level are chosen using
round robin. Each packet will traverse MDQ, TL4 to TL1 levels.
Each level contains an array of queues to support scheduling and
shaping.

As HTB supports classful queuing mechanism by supporting rate and
ceil and allow the user to control the absolute bandwidth to
particular classes of traffic the same can be achieved by
configuring shapers and schedulers on different transmit levels.

This series of patches adds support for HTB offload,

Patch1: Allow strict priority parameter in HTB offload mode.

Patch2: Rename existing total tx queues for better readability

Patch3: defines APIs such that the driver can dynamically initialize/
        deinitialize the send queues.

Patch4: Refactors transmit alloc/free calls as preparation for QOS
        offload code.

Patch5: moves rate limiting logic to common header which will be used
        by qos offload code.

Patch6: Adds actual HTB offload support.

Patch7: exposes qos send queue stats over ethtool.

Patch8: Add documentation about htb offload flow in driver
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2023-05-15 09:31:08 +01:00
commit 57f1915fc7
20 changed files with 2144 additions and 127 deletions

View file

@ -13,6 +13,7 @@ Contents
- `Drivers`_
- `Basic packet flow`_
- `Devlink health reporters`_
- `Quality of service`_
Overview
========
@ -287,3 +288,47 @@ For example::
NIX_AF_ERR:
NIX Error Interrupt Reg : 64
Rx on unmapped PF_FUNC
Quality of service
==================
Hardware algorithms used in scheduling
--------------------------------------
octeontx2 silicon and CN10K transmit interface consists of five transmit levels
starting from SMQ/MDQ, TL4 to TL1. Each packet will traverse MDQ, TL4 to TL1
levels. Each level contains an array of queues to support scheduling and shaping.
The hardware uses the below algorithms depending on the priority of scheduler queues.
once the usercreates tc classes with different priorities, the driver configures
schedulers allocated to the class with specified priority along with rate-limiting
configuration.
1. Strict Priority
- Once packets are submitted to MDQ, hardware picks all active MDQs having different priority
using strict priority.
2. Round Robin
- Active MDQs having the same priority level are chosen using round robin.
Setup HTB offload
-----------------
1. Enable HW TC offload on the interface::
# ethtool -K <interface> hw-tc-offload on
2. Crate htb root::
# tc qdisc add dev <interface> clsact
# tc qdisc replace dev <interface> root handle 1: htb offload
3. Create tc classes with different priorities::
# tc class add dev <interface> parent 1: classid 1:1 htb rate 10Gbit prio 1
# tc class add dev <interface> parent 1: classid 1:2 htb rate 10Gbit prio 7

View file

@ -142,7 +142,7 @@ enum nix_scheduler {
#define TXSCH_RR_QTM_MAX ((1 << 24) - 1)
#define TXSCH_TL1_DFLT_RR_QTM TXSCH_RR_QTM_MAX
#define TXSCH_TL1_DFLT_RR_PRIO (0x1ull)
#define TXSCH_TL1_DFLT_RR_PRIO (0x7ull)
#define CN10K_MAX_DWRR_WEIGHT 16384 /* Weight is 14bit on CN10K */
/* Min/Max packet sizes, excluding FCS */

View file

@ -1222,6 +1222,11 @@ static int rvu_dbg_npa_ctx_display(struct seq_file *m, void *unused, int ctype)
for (aura = id; aura < max_id; aura++) {
aq_req.aura_id = aura;
/* Skip if queue is uninitialized */
if (ctype == NPA_AQ_CTYPE_POOL && !test_bit(aura, pfvf->pool_bmap))
continue;
seq_printf(m, "======%s : %d=======\n",
(ctype == NPA_AQ_CTYPE_AURA) ? "AURA" : "POOL",
aq_req.aura_id);

View file

@ -1691,6 +1691,42 @@ handle_txschq_shaper_update(struct rvu *rvu, int blkaddr, int nixlf,
return true;
}
static void nix_reset_tx_schedule(struct rvu *rvu, int blkaddr,
int lvl, int schq)
{
u64 tlx_parent = 0, tlx_schedule = 0;
switch (lvl) {
case NIX_TXSCH_LVL_TL2:
tlx_parent = NIX_AF_TL2X_PARENT(schq);
tlx_schedule = NIX_AF_TL2X_SCHEDULE(schq);
break;
case NIX_TXSCH_LVL_TL3:
tlx_parent = NIX_AF_TL3X_PARENT(schq);
tlx_schedule = NIX_AF_TL3X_SCHEDULE(schq);
break;
case NIX_TXSCH_LVL_TL4:
tlx_parent = NIX_AF_TL4X_PARENT(schq);
tlx_schedule = NIX_AF_TL4X_SCHEDULE(schq);
break;
case NIX_TXSCH_LVL_MDQ:
/* no need to reset SMQ_CFG as HW clears this CSR
* on SMQ flush
*/
tlx_parent = NIX_AF_MDQX_PARENT(schq);
tlx_schedule = NIX_AF_MDQX_SCHEDULE(schq);
break;
default:
return;
}
if (tlx_parent)
rvu_write64(rvu, blkaddr, tlx_parent, 0x0);
if (tlx_schedule)
rvu_write64(rvu, blkaddr, tlx_schedule, 0x0);
}
/* Disable shaping of pkts by a scheduler queue
* at a given scheduler level.
*/
@ -2039,6 +2075,7 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
pfvf_map[schq] = TXSCH_MAP(pcifunc, 0);
nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
nix_reset_tx_shaping(rvu, blkaddr, nixlf, lvl, schq);
nix_reset_tx_schedule(rvu, blkaddr, lvl, schq);
}
for (idx = 0; idx < req->schq[lvl]; idx++) {
@ -2048,6 +2085,7 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
pfvf_map[schq] = TXSCH_MAP(pcifunc, 0);
nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
nix_reset_tx_shaping(rvu, blkaddr, nixlf, lvl, schq);
nix_reset_tx_schedule(rvu, blkaddr, lvl, schq);
}
}
@ -2143,6 +2181,7 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
continue;
nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
nix_clear_tx_xoff(rvu, blkaddr, lvl, schq);
nix_reset_tx_shaping(rvu, blkaddr, nixlf, lvl, schq);
}
}
nix_clear_tx_xoff(rvu, blkaddr, NIX_TXSCH_LVL_TL1,
@ -2181,6 +2220,7 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
for (schq = 0; schq < txsch->schq.max; schq++) {
if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
continue;
nix_reset_tx_schedule(rvu, blkaddr, lvl, schq);
rvu_free_rsrc(&txsch->schq, schq);
txsch->pfvf_map[schq] = TXSCH_MAP(0, NIX_TXSCHQ_FREE);
}
@ -2240,6 +2280,9 @@ static int nix_txschq_free_one(struct rvu *rvu,
*/
nix_clear_tx_xoff(rvu, blkaddr, lvl, schq);
nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
nix_reset_tx_shaping(rvu, blkaddr, nixlf, lvl, schq);
/* Flush if it is a SMQ. Onus of disabling
* TL2/3 queue links before SMQ flush is on user
*/
@ -2249,6 +2292,8 @@ static int nix_txschq_free_one(struct rvu *rvu,
goto err;
}
nix_reset_tx_schedule(rvu, blkaddr, lvl, schq);
/* Free the resource */
rvu_free_rsrc(&txsch->schq, schq);
txsch->pfvf_map[schq] = TXSCH_MAP(0, NIX_TXSCHQ_FREE);

View file

@ -8,7 +8,7 @@ obj-$(CONFIG_OCTEONTX2_VF) += rvu_nicvf.o otx2_ptp.o
rvu_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \
otx2_flows.o otx2_tc.o cn10k.o otx2_dmac_flt.o \
otx2_devlink.o
otx2_devlink.o qos_sq.o qos.o
rvu_nicvf-y := otx2_vf.o otx2_devlink.o
rvu_nicpf-$(CONFIG_DCB) += otx2_dcbnl.o

View file

@ -89,6 +89,11 @@ int otx2_update_sq_stats(struct otx2_nic *pfvf, int qidx)
if (!pfvf->qset.sq)
return 0;
if (qidx >= pfvf->hw.non_qos_queues) {
if (!test_bit(qidx - pfvf->hw.non_qos_queues, pfvf->qos.qos_sq_bmap))
return 0;
}
otx2_nix_sq_op_stats(&sq->stats, pfvf, qidx);
return 1;
}
@ -513,8 +518,8 @@ void otx2_config_irq_coalescing(struct otx2_nic *pfvf, int qidx)
(pfvf->hw.cq_ecount_wait - 1));
}
int __otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool,
dma_addr_t *dma)
static int __otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool,
dma_addr_t *dma)
{
u8 *buf;
@ -532,8 +537,8 @@ int __otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool,
return 0;
}
static int otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool,
dma_addr_t *dma)
int otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool,
dma_addr_t *dma)
{
int ret;
@ -716,7 +721,8 @@ EXPORT_SYMBOL(otx2_smq_flush);
int otx2_txsch_alloc(struct otx2_nic *pfvf)
{
struct nix_txsch_alloc_req *req;
int lvl;
struct nix_txsch_alloc_rsp *rsp;
int lvl, schq, rc;
/* Get memory to put this msg */
req = otx2_mbox_alloc_msg_nix_txsch_alloc(&pfvf->mbox);
@ -726,43 +732,83 @@ int otx2_txsch_alloc(struct otx2_nic *pfvf)
/* Request one schq per level */
for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++)
req->schq[lvl] = 1;
rc = otx2_sync_mbox_msg(&pfvf->mbox);
if (rc)
return rc;
return otx2_sync_mbox_msg(&pfvf->mbox);
rsp = (struct nix_txsch_alloc_rsp *)
otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr);
if (IS_ERR(rsp))
return PTR_ERR(rsp);
/* Setup transmit scheduler list */
for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++)
for (schq = 0; schq < rsp->schq[lvl]; schq++)
pfvf->hw.txschq_list[lvl][schq] =
rsp->schq_list[lvl][schq];
pfvf->hw.txschq_link_cfg_lvl = rsp->link_cfg_lvl;
return 0;
}
int otx2_txschq_stop(struct otx2_nic *pfvf)
void otx2_txschq_free_one(struct otx2_nic *pfvf, u16 lvl, u16 schq)
{
struct nix_txsch_free_req *free_req;
int lvl, schq, err;
int err;
mutex_lock(&pfvf->mbox.lock);
/* Free the transmit schedulers */
free_req = otx2_mbox_alloc_msg_nix_txsch_free(&pfvf->mbox);
if (!free_req) {
mutex_unlock(&pfvf->mbox.lock);
return -ENOMEM;
netdev_err(pfvf->netdev,
"Failed alloc txschq free req\n");
return;
}
free_req->flags = TXSCHQ_FREE_ALL;
free_req->schq_lvl = lvl;
free_req->schq = schq;
err = otx2_sync_mbox_msg(&pfvf->mbox);
if (err) {
netdev_err(pfvf->netdev,
"Failed stop txschq %d at level %d\n", schq, lvl);
}
mutex_unlock(&pfvf->mbox.lock);
}
void otx2_txschq_stop(struct otx2_nic *pfvf)
{
int lvl, schq;
/* free non QOS TLx nodes */
for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++)
otx2_txschq_free_one(pfvf, lvl,
pfvf->hw.txschq_list[lvl][0]);
/* Clear the txschq list */
for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
for (schq = 0; schq < MAX_TXSCHQ_PER_FUNC; schq++)
pfvf->hw.txschq_list[lvl][schq] = 0;
}
return err;
}
void otx2_sqb_flush(struct otx2_nic *pfvf)
{
int qidx, sqe_tail, sqe_head;
struct otx2_snd_queue *sq;
u64 incr, *ptr, val;
int timeout = 1000;
ptr = (u64 *)otx2_get_regaddr(pfvf, NIX_LF_SQ_OP_STATUS);
for (qidx = 0; qidx < pfvf->hw.tot_tx_queues; qidx++) {
for (qidx = 0; qidx < otx2_get_total_tx_queues(pfvf); qidx++) {
sq = &pfvf->qset.sq[qidx];
if (!sq->sqb_ptrs)
continue;
incr = (u64)qidx << 32;
while (timeout) {
val = otx2_atomic64_add(incr, ptr);
@ -862,7 +908,7 @@ int otx2_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura)
return otx2_sync_mbox_msg(&pfvf->mbox);
}
static int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura)
int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura)
{
struct otx2_qset *qset = &pfvf->qset;
struct otx2_snd_queue *sq;
@ -935,9 +981,17 @@ static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx)
cq->cint_idx = qidx - pfvf->hw.rx_queues;
cq->cqe_cnt = qset->sqe_cnt;
} else {
cq->cq_type = CQ_XDP;
cq->cint_idx = qidx - non_xdp_queues;
cq->cqe_cnt = qset->sqe_cnt;
if (pfvf->hw.xdp_queues &&
qidx < non_xdp_queues + pfvf->hw.xdp_queues) {
cq->cq_type = CQ_XDP;
cq->cint_idx = qidx - non_xdp_queues;
cq->cqe_cnt = qset->sqe_cnt;
} else {
cq->cq_type = CQ_QOS;
cq->cint_idx = qidx - non_xdp_queues -
pfvf->hw.xdp_queues;
cq->cqe_cnt = qset->sqe_cnt;
}
}
cq->cqe_size = pfvf->qset.xqe_size;
@ -1048,7 +1102,7 @@ int otx2_config_nix_queues(struct otx2_nic *pfvf)
}
/* Initialize TX queues */
for (qidx = 0; qidx < pfvf->hw.tot_tx_queues; qidx++) {
for (qidx = 0; qidx < pfvf->hw.non_qos_queues; qidx++) {
u16 sqb_aura = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx);
err = otx2_sq_init(pfvf, qidx, sqb_aura);
@ -1095,7 +1149,7 @@ int otx2_config_nix(struct otx2_nic *pfvf)
/* Set RQ/SQ/CQ counts */
nixlf->rq_cnt = pfvf->hw.rx_queues;
nixlf->sq_cnt = pfvf->hw.tot_tx_queues;
nixlf->sq_cnt = otx2_get_total_tx_queues(pfvf);
nixlf->cq_cnt = pfvf->qset.cq_cnt;
nixlf->rss_sz = MAX_RSS_INDIR_TBL_SIZE;
nixlf->rss_grps = MAX_RSS_GROUPS;
@ -1133,7 +1187,7 @@ void otx2_sq_free_sqbs(struct otx2_nic *pfvf)
int sqb, qidx;
u64 iova, pa;
for (qidx = 0; qidx < hw->tot_tx_queues; qidx++) {
for (qidx = 0; qidx < otx2_get_total_tx_queues(pfvf); qidx++) {
sq = &qset->sq[qidx];
if (!sq->sqb_ptrs)
continue;
@ -1201,8 +1255,8 @@ void otx2_aura_pool_free(struct otx2_nic *pfvf)
pfvf->qset.pool = NULL;
}
static int otx2_aura_init(struct otx2_nic *pfvf, int aura_id,
int pool_id, int numptrs)
int otx2_aura_init(struct otx2_nic *pfvf, int aura_id,
int pool_id, int numptrs)
{
struct npa_aq_enq_req *aq;
struct otx2_pool *pool;
@ -1278,8 +1332,8 @@ static int otx2_aura_init(struct otx2_nic *pfvf, int aura_id,
return 0;
}
static int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id,
int stack_pages, int numptrs, int buf_size)
int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id,
int stack_pages, int numptrs, int buf_size)
{
struct npa_aq_enq_req *aq;
struct otx2_pool *pool;
@ -1349,7 +1403,7 @@ int otx2_sq_aura_pool_init(struct otx2_nic *pfvf)
stack_pages =
(num_sqbs + hw->stack_pg_ptrs - 1) / hw->stack_pg_ptrs;
for (qidx = 0; qidx < hw->tot_tx_queues; qidx++) {
for (qidx = 0; qidx < hw->non_qos_queues; qidx++) {
pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx);
/* Initialize aura context */
err = otx2_aura_init(pfvf, pool_id, pool_id, num_sqbs);
@ -1369,7 +1423,7 @@ int otx2_sq_aura_pool_init(struct otx2_nic *pfvf)
goto fail;
/* Allocate pointers and free them to aura/pool */
for (qidx = 0; qidx < hw->tot_tx_queues; qidx++) {
for (qidx = 0; qidx < hw->non_qos_queues; qidx++) {
pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx);
pool = &pfvf->qset.pool[pool_id];
@ -1629,21 +1683,6 @@ void mbox_handler_cgx_fec_stats(struct otx2_nic *pfvf,
pfvf->hw.cgx_fec_uncorr_blks += rsp->fec_uncorr_blks;
}
void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf,
struct nix_txsch_alloc_rsp *rsp)
{
int lvl, schq;
/* Setup transmit scheduler list */
for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++)
for (schq = 0; schq < rsp->schq[lvl]; schq++)
pf->hw.txschq_list[lvl][schq] =
rsp->schq_list[lvl][schq];
pf->hw.txschq_link_cfg_lvl = rsp->link_cfg_lvl;
}
EXPORT_SYMBOL(mbox_handler_nix_txsch_alloc);
void mbox_handler_npa_lf_alloc(struct otx2_nic *pfvf,
struct npa_lf_alloc_rsp *rsp)
{

View file

@ -28,6 +28,7 @@
#include "otx2_txrx.h"
#include "otx2_devlink.h"
#include <rvu_trace.h>
#include "qos.h"
/* IPv4 flag more fragment bit */
#define IPV4_FLAG_MORE 0x20
@ -184,13 +185,29 @@ struct mbox {
int up_num_msgs; /* mbox_up number of messages */
};
/* Egress rate limiting definitions */
#define MAX_BURST_EXPONENT 0x0FULL
#define MAX_BURST_MANTISSA 0xFFULL
#define MAX_BURST_SIZE 130816ULL
#define MAX_RATE_DIVIDER_EXPONENT 12ULL
#define MAX_RATE_EXPONENT 0x0FULL
#define MAX_RATE_MANTISSA 0xFFULL
/* Bitfields in NIX_TLX_PIR register */
#define TLX_RATE_MANTISSA GENMASK_ULL(8, 1)
#define TLX_RATE_EXPONENT GENMASK_ULL(12, 9)
#define TLX_RATE_DIVIDER_EXPONENT GENMASK_ULL(16, 13)
#define TLX_BURST_MANTISSA GENMASK_ULL(36, 29)
#define TLX_BURST_EXPONENT GENMASK_ULL(40, 37)
struct otx2_hw {
struct pci_dev *pdev;
struct otx2_rss_info rss_info;
u16 rx_queues;
u16 tx_queues;
u16 xdp_queues;
u16 tot_tx_queues;
u16 tc_tx_queues;
u16 non_qos_queues; /* tx queues plus xdp queues */
u16 max_queues;
u16 pool_cnt;
u16 rqpool_cnt;
@ -251,6 +268,7 @@ struct otx2_hw {
#define CN10K_RPM 3
#define CN10K_PTP_ONESTEP 4
#define CN10K_HW_MACSEC 5
#define QOS_CIR_PIR_SUPPORT 6
unsigned long cap_flag;
#define LMT_LINE_SIZE 128
@ -506,6 +524,8 @@ struct otx2_nic {
u16 pfc_schq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC];
bool pfc_alloc_status[NIX_PF_PFC_PRIO_MAX];
#endif
/* qos */
struct otx2_qos qos;
/* napi event count. It is needed for adaptive irq coalescing. */
u32 napi_events;
@ -587,6 +607,7 @@ static inline void otx2_setup_dev_hw_settings(struct otx2_nic *pfvf)
__set_bit(CN10K_LMTST, &hw->cap_flag);
__set_bit(CN10K_RPM, &hw->cap_flag);
__set_bit(CN10K_PTP_ONESTEP, &hw->cap_flag);
__set_bit(QOS_CIR_PIR_SUPPORT, &hw->cap_flag);
}
if (is_dev_cn10kb(pfvf->pdev))
@ -750,8 +771,7 @@ static inline void cn10k_aura_freeptr(void *dev, int aura, u64 buf)
/* Alloc pointer from pool/aura */
static inline u64 otx2_aura_allocptr(struct otx2_nic *pfvf, int aura)
{
u64 *ptr = (u64 *)otx2_get_regaddr(pfvf,
NPA_LF_AURA_OP_ALLOCX(0));
u64 *ptr = (__force u64 *)otx2_get_regaddr(pfvf, NPA_LF_AURA_OP_ALLOCX(0));
u64 incr = (u64)aura | BIT_ULL(63);
return otx2_atomic64_add(incr, ptr);
@ -893,12 +913,34 @@ static inline void otx2_dma_unmap_page(struct otx2_nic *pfvf,
static inline u16 otx2_get_smq_idx(struct otx2_nic *pfvf, u16 qidx)
{
u16 smq;
#ifdef CONFIG_DCB
if (qidx < NIX_PF_PFC_PRIO_MAX && pfvf->pfc_alloc_status[qidx])
return pfvf->pfc_schq_list[NIX_TXSCH_LVL_SMQ][qidx];
#endif
/* check if qidx falls under QOS queues */
if (qidx >= pfvf->hw.non_qos_queues)
smq = pfvf->qos.qid_to_sqmap[qidx - pfvf->hw.non_qos_queues];
else
smq = pfvf->hw.txschq_list[NIX_TXSCH_LVL_SMQ][0];
return pfvf->hw.txschq_list[NIX_TXSCH_LVL_SMQ][0];
return smq;
}
static inline u16 otx2_get_total_tx_queues(struct otx2_nic *pfvf)
{
return pfvf->hw.non_qos_queues + pfvf->hw.tc_tx_queues;
}
static inline u64 otx2_convert_rate(u64 rate)
{
u64 converted_rate;
/* Convert bytes per second to Mbps */
converted_rate = rate * 8;
converted_rate = max_t(u64, converted_rate / 1000000, 1);
return converted_rate;
}
/* MSI-X APIs */
@ -925,19 +967,25 @@ int otx2_config_nix(struct otx2_nic *pfvf);
int otx2_config_nix_queues(struct otx2_nic *pfvf);
int otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool pfc_en);
int otx2_txsch_alloc(struct otx2_nic *pfvf);
int otx2_txschq_stop(struct otx2_nic *pfvf);
void otx2_txschq_stop(struct otx2_nic *pfvf);
void otx2_txschq_free_one(struct otx2_nic *pfvf, u16 lvl, u16 schq);
void otx2_sqb_flush(struct otx2_nic *pfvf);
int __otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool,
dma_addr_t *dma);
int otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool,
dma_addr_t *dma);
int otx2_rxtx_enable(struct otx2_nic *pfvf, bool enable);
void otx2_ctx_disable(struct mbox *mbox, int type, bool npa);
int otx2_nix_config_bp(struct otx2_nic *pfvf, bool enable);
void otx2_cleanup_rx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq);
void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq);
int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura);
int otx2_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura);
int cn10k_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura);
int otx2_alloc_buffer(struct otx2_nic *pfvf, struct otx2_cq_queue *cq,
dma_addr_t *dma);
int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id,
int stack_pages, int numptrs, int buf_size);
int otx2_aura_init(struct otx2_nic *pfvf, int aura_id,
int pool_id, int numptrs);
/* RSS configuration APIs*/
int otx2_rss_init(struct otx2_nic *pfvf);
@ -1045,4 +1093,24 @@ static inline void cn10k_handle_mcs_event(struct otx2_nic *pfvf,
{}
#endif /* CONFIG_MACSEC */
/* qos support */
static inline void otx2_qos_init(struct otx2_nic *pfvf, int qos_txqs)
{
struct otx2_hw *hw = &pfvf->hw;
hw->tc_tx_queues = qos_txqs;
INIT_LIST_HEAD(&pfvf->qos.qos_tree);
mutex_init(&pfvf->qos.qos_lock);
}
static inline void otx2_shutdown_qos(struct otx2_nic *pfvf)
{
mutex_destroy(&pfvf->qos.qos_lock);
}
u16 otx2_select_queue(struct net_device *netdev, struct sk_buff *skb,
struct net_device *sb_dev);
int otx2_get_txq_by_classid(struct otx2_nic *pfvf, u16 classid);
void otx2_qos_config_txschq(struct otx2_nic *pfvf);
void otx2_clean_qos_queues(struct otx2_nic *pfvf);
#endif /* OTX2_COMMON_H */

View file

@ -92,10 +92,16 @@ static void otx2_get_qset_strings(struct otx2_nic *pfvf, u8 **data, int qset)
*data += ETH_GSTRING_LEN;
}
}
for (qidx = 0; qidx < pfvf->hw.tx_queues; qidx++) {
for (qidx = 0; qidx < otx2_get_total_tx_queues(pfvf); qidx++) {
for (stats = 0; stats < otx2_n_queue_stats; stats++) {
sprintf(*data, "txq%d: %s", qidx + start_qidx,
otx2_queue_stats[stats].name);
if (qidx >= pfvf->hw.non_qos_queues)
sprintf(*data, "txq_qos%d: %s",
qidx + start_qidx - pfvf->hw.non_qos_queues,
otx2_queue_stats[stats].name);
else
sprintf(*data, "txq%d: %s", qidx + start_qidx,
otx2_queue_stats[stats].name);
*data += ETH_GSTRING_LEN;
}
}
@ -159,7 +165,7 @@ static void otx2_get_qset_stats(struct otx2_nic *pfvf,
[otx2_queue_stats[stat].index];
}
for (qidx = 0; qidx < pfvf->hw.tx_queues; qidx++) {
for (qidx = 0; qidx < otx2_get_total_tx_queues(pfvf); qidx++) {
if (!otx2_update_sq_stats(pfvf, qidx)) {
for (stat = 0; stat < otx2_n_queue_stats; stat++)
*((*data)++) = 0;
@ -254,7 +260,7 @@ static int otx2_get_sset_count(struct net_device *netdev, int sset)
return -EINVAL;
qstats_count = otx2_n_queue_stats *
(pfvf->hw.rx_queues + pfvf->hw.tx_queues);
(pfvf->hw.rx_queues + otx2_get_total_tx_queues(pfvf));
if (!test_bit(CN10K_RPM, &pfvf->hw.cap_flag))
mac_stats = CGX_RX_STATS_COUNT + CGX_TX_STATS_COUNT;
otx2_update_lmac_fec_stats(pfvf);
@ -282,7 +288,7 @@ static int otx2_set_channels(struct net_device *dev,
{
struct otx2_nic *pfvf = netdev_priv(dev);
bool if_up = netif_running(dev);
int err = 0;
int err, qos_txqs;
if (!channel->rx_count || !channel->tx_count)
return -EINVAL;
@ -296,14 +302,19 @@ static int otx2_set_channels(struct net_device *dev,
if (if_up)
dev->netdev_ops->ndo_stop(dev);
err = otx2_set_real_num_queues(dev, channel->tx_count,
qos_txqs = bitmap_weight(pfvf->qos.qos_sq_bmap,
OTX2_QOS_MAX_LEAF_NODES);
err = otx2_set_real_num_queues(dev, channel->tx_count + qos_txqs,
channel->rx_count);
if (err)
return err;
pfvf->hw.rx_queues = channel->rx_count;
pfvf->hw.tx_queues = channel->tx_count;
pfvf->qset.cq_cnt = pfvf->hw.tx_queues + pfvf->hw.rx_queues;
if (pfvf->xdp_prog)
pfvf->hw.xdp_queues = channel->rx_count;
pfvf->hw.non_qos_queues = pfvf->hw.tx_queues + pfvf->hw.xdp_queues;
if (if_up)
err = dev->netdev_ops->ndo_open(dev);
@ -1405,7 +1416,7 @@ static int otx2vf_get_sset_count(struct net_device *netdev, int sset)
return -EINVAL;
qstats_count = otx2_n_queue_stats *
(vf->hw.rx_queues + vf->hw.tx_queues);
(vf->hw.rx_queues + otx2_get_total_tx_queues(vf));
return otx2_n_dev_stats + otx2_n_drv_stats + qstats_count + 1;
}

View file

@ -23,6 +23,7 @@
#include "otx2_struct.h"
#include "otx2_ptp.h"
#include "cn10k.h"
#include "qos.h"
#include <rvu_trace.h>
#define DRV_NAME "rvu_nicpf"
@ -791,10 +792,6 @@ static void otx2_process_pfaf_mbox_msg(struct otx2_nic *pf,
case MBOX_MSG_NIX_LF_ALLOC:
mbox_handler_nix_lf_alloc(pf, (struct nix_lf_alloc_rsp *)msg);
break;
case MBOX_MSG_NIX_TXSCH_ALLOC:
mbox_handler_nix_txsch_alloc(pf,
(struct nix_txsch_alloc_rsp *)msg);
break;
case MBOX_MSG_NIX_BP_ENABLE:
mbox_handler_nix_bp_enable(pf, (struct nix_bp_cfg_rsp *)msg);
break;
@ -1228,6 +1225,7 @@ static char *nix_snd_status_e_str[NIX_SND_STATUS_MAX] = {
static irqreturn_t otx2_q_intr_handler(int irq, void *data)
{
struct otx2_nic *pf = data;
struct otx2_snd_queue *sq;
u64 val, *ptr;
u64 qidx = 0;
@ -1257,10 +1255,14 @@ static irqreturn_t otx2_q_intr_handler(int irq, void *data)
}
/* SQ */
for (qidx = 0; qidx < pf->hw.tot_tx_queues; qidx++) {
for (qidx = 0; qidx < otx2_get_total_tx_queues(pf); qidx++) {
u64 sq_op_err_dbg, mnq_err_dbg, snd_err_dbg;
u8 sq_op_err_code, mnq_err_code, snd_err_code;
sq = &pf->qset.sq[qidx];
if (!sq->sqb_ptrs)
continue;
/* Below debug registers captures first errors corresponding to
* those registers. We don't have to check against SQ qid as
* these are fatal errors.
@ -1383,8 +1385,11 @@ static void otx2_free_sq_res(struct otx2_nic *pf)
otx2_ctx_disable(&pf->mbox, NIX_AQ_CTYPE_SQ, false);
/* Free SQB pointers */
otx2_sq_free_sqbs(pf);
for (qidx = 0; qidx < pf->hw.tot_tx_queues; qidx++) {
for (qidx = 0; qidx < otx2_get_total_tx_queues(pf); qidx++) {
sq = &qset->sq[qidx];
/* Skip freeing Qos queues if they are not initialized */
if (!sq->sqe)
continue;
qmem_free(pf->dev, sq->sqe);
qmem_free(pf->dev, sq->tso_hdrs);
kfree(sq->sg);
@ -1433,7 +1438,7 @@ static int otx2_init_hw_resources(struct otx2_nic *pf)
* so, aura count = pool count.
*/
hw->rqpool_cnt = hw->rx_queues;
hw->sqpool_cnt = hw->tot_tx_queues;
hw->sqpool_cnt = otx2_get_total_tx_queues(pf);
hw->pool_cnt = hw->rqpool_cnt + hw->sqpool_cnt;
/* Maximum hardware supported transmit length */
@ -1516,8 +1521,7 @@ static int otx2_init_hw_resources(struct otx2_nic *pf)
otx2_free_cq_res(pf);
otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false);
err_free_txsch:
if (otx2_txschq_stop(pf))
dev_err(pf->dev, "%s failed to stop TX schedulers\n", __func__);
otx2_txschq_stop(pf);
err_free_sq_ptrs:
otx2_sq_free_sqbs(pf);
err_free_rq_ptrs:
@ -1552,21 +1556,21 @@ static void otx2_free_hw_resources(struct otx2_nic *pf)
struct mbox *mbox = &pf->mbox;
struct otx2_cq_queue *cq;
struct msg_req *req;
int qidx, err;
int qidx;
/* Ensure all SQE are processed */
otx2_sqb_flush(pf);
/* Stop transmission */
err = otx2_txschq_stop(pf);
if (err)
dev_err(pf->dev, "RVUPF: Failed to stop/free TX schedulers\n");
otx2_txschq_stop(pf);
#ifdef CONFIG_DCB
if (pf->pfc_en)
otx2_pfc_txschq_stop(pf);
#endif
otx2_clean_qos_queues(pf);
mutex_lock(&mbox->lock);
/* Disable backpressure */
if (!(pf->pcifunc & RVU_PFVF_FUNC_MASK))
@ -1688,11 +1692,14 @@ int otx2_open(struct net_device *netdev)
netif_carrier_off(netdev);
pf->qset.cq_cnt = pf->hw.rx_queues + pf->hw.tot_tx_queues;
/* RQ and SQs are mapped to different CQs,
* so find out max CQ IRQs (i.e CINTs) needed.
*/
pf->hw.cint_cnt = max(pf->hw.rx_queues, pf->hw.tx_queues);
pf->hw.cint_cnt = max3(pf->hw.rx_queues, pf->hw.tx_queues,
pf->hw.tc_tx_queues);
pf->qset.cq_cnt = pf->hw.rx_queues + otx2_get_total_tx_queues(pf);
qset->napi = kcalloc(pf->hw.cint_cnt, sizeof(*cq_poll), GFP_KERNEL);
if (!qset->napi)
return -ENOMEM;
@ -1708,7 +1715,7 @@ int otx2_open(struct net_device *netdev)
if (!qset->cq)
goto err_free_mem;
qset->sq = kcalloc(pf->hw.tot_tx_queues,
qset->sq = kcalloc(otx2_get_total_tx_queues(pf),
sizeof(struct otx2_snd_queue), GFP_KERNEL);
if (!qset->sq)
goto err_free_mem;
@ -1743,6 +1750,11 @@ int otx2_open(struct net_device *netdev)
else
cq_poll->cq_ids[CQ_XDP] = CINT_INVALID_CQ;
cq_poll->cq_ids[CQ_QOS] = (qidx < pf->hw.tc_tx_queues) ?
(qidx + pf->hw.rx_queues +
pf->hw.non_qos_queues) :
CINT_INVALID_CQ;
cq_poll->dev = (void *)pf;
cq_poll->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_CQE;
INIT_WORK(&cq_poll->dim.work, otx2_dim_work);
@ -1826,6 +1838,9 @@ int otx2_open(struct net_device *netdev)
/* 'intf_down' may be checked on any cpu */
smp_wmb();
/* Enable QoS configuration before starting tx queues */
otx2_qos_config_txschq(pf);
/* we have already received link status notification */
if (pf->linfo.link_up && !(pf->pcifunc & RVU_PFVF_FUNC_MASK))
otx2_handle_link_event(pf);
@ -1947,6 +1962,12 @@ static netdev_tx_t otx2_xmit(struct sk_buff *skb, struct net_device *netdev)
int qidx = skb_get_queue_mapping(skb);
struct otx2_snd_queue *sq;
struct netdev_queue *txq;
int sq_idx;
/* XDP SQs are not mapped with TXQs
* advance qid to derive correct sq mapped with QOS
*/
sq_idx = (qidx >= pf->hw.tx_queues) ? (qidx + pf->hw.xdp_queues) : qidx;
/* Check for minimum and maximum packet length */
if (skb->len <= ETH_HLEN ||
@ -1955,7 +1976,7 @@ static netdev_tx_t otx2_xmit(struct sk_buff *skb, struct net_device *netdev)
return NETDEV_TX_OK;
}
sq = &pf->qset.sq[qidx];
sq = &pf->qset.sq[sq_idx];
txq = netdev_get_tx_queue(netdev, qidx);
if (!otx2_sq_append_skb(netdev, sq, skb, qidx)) {
@ -1973,14 +1994,48 @@ static netdev_tx_t otx2_xmit(struct sk_buff *skb, struct net_device *netdev)
return NETDEV_TX_OK;
}
static u16 otx2_select_queue(struct net_device *netdev, struct sk_buff *skb,
struct net_device *sb_dev)
static int otx2_qos_select_htb_queue(struct otx2_nic *pf, struct sk_buff *skb,
u16 htb_maj_id)
{
u16 classid;
if ((TC_H_MAJ(skb->priority) >> 16) == htb_maj_id)
classid = TC_H_MIN(skb->priority);
else
classid = READ_ONCE(pf->qos.defcls);
if (!classid)
return 0;
return otx2_get_txq_by_classid(pf, classid);
}
u16 otx2_select_queue(struct net_device *netdev, struct sk_buff *skb,
struct net_device *sb_dev)
{
#ifdef CONFIG_DCB
struct otx2_nic *pf = netdev_priv(netdev);
bool qos_enabled;
#ifdef CONFIG_DCB
u8 vlan_prio;
#endif
int txq;
qos_enabled = (netdev->real_num_tx_queues > pf->hw.tx_queues) ? true : false;
if (unlikely(qos_enabled)) {
/* This smp_load_acquire() pairs with smp_store_release() in
* otx2_qos_root_add() called from htb offload root creation
*/
u16 htb_maj_id = smp_load_acquire(&pf->qos.maj_id);
if (unlikely(htb_maj_id)) {
txq = otx2_qos_select_htb_queue(pf, skb, htb_maj_id);
if (txq > 0)
return txq;
goto process_pfc;
}
}
process_pfc:
#ifdef CONFIG_DCB
if (!skb_vlan_tag_present(skb))
goto pick_tx;
@ -1994,8 +2049,13 @@ static u16 otx2_select_queue(struct net_device *netdev, struct sk_buff *skb,
pick_tx:
#endif
return netdev_pick_tx(netdev, skb, NULL);
txq = netdev_pick_tx(netdev, skb, NULL);
if (unlikely(qos_enabled))
return txq % pf->hw.tx_queues;
return txq;
}
EXPORT_SYMBOL(otx2_select_queue);
static netdev_features_t otx2_fix_features(struct net_device *dev,
netdev_features_t features)
@ -2529,7 +2589,7 @@ static int otx2_xdp_setup(struct otx2_nic *pf, struct bpf_prog *prog)
xdp_features_clear_redirect_target(dev);
}
pf->hw.tot_tx_queues += pf->hw.xdp_queues;
pf->hw.non_qos_queues += pf->hw.xdp_queues;
if (if_up)
otx2_open(pf->netdev);
@ -2712,10 +2772,10 @@ static void otx2_sriov_vfcfg_cleanup(struct otx2_nic *pf)
static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct device *dev = &pdev->dev;
int err, qcount, qos_txqs;
struct net_device *netdev;
struct otx2_nic *pf;
struct otx2_hw *hw;
int err, qcount;
int num_vec;
err = pcim_enable_device(pdev);
@ -2740,8 +2800,9 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* Set number of queues */
qcount = min_t(int, num_online_cpus(), OTX2_MAX_CQ_CNT);
qos_txqs = min_t(int, qcount, OTX2_QOS_MAX_LEAF_NODES);
netdev = alloc_etherdev_mqs(sizeof(*pf), qcount, qcount);
netdev = alloc_etherdev_mqs(sizeof(*pf), qcount + qos_txqs, qcount);
if (!netdev) {
err = -ENOMEM;
goto err_release_regions;
@ -2760,7 +2821,7 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)
hw->pdev = pdev;
hw->rx_queues = qcount;
hw->tx_queues = qcount;
hw->tot_tx_queues = qcount;
hw->non_qos_queues = qcount;
hw->max_queues = qcount;
hw->rbuf_len = OTX2_DEFAULT_RBUF_LEN;
/* Use CQE of 128 byte descriptor size by default */
@ -2929,6 +2990,8 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_pf_sriov_init;
#endif
otx2_qos_init(pf, qos_txqs);
return 0;
err_pf_sriov_init:
@ -3104,6 +3167,7 @@ static void otx2_remove(struct pci_dev *pdev)
otx2_ptp_destroy(pf);
otx2_mcam_flow_del(pf);
otx2_shutdown_tc(pf);
otx2_shutdown_qos(pf);
otx2_detach_resources(&pf->mbox);
if (pf->hw.lmt_info)
free_percpu(pf->hw.lmt_info);

View file

@ -145,12 +145,25 @@
#define NIX_AF_TL1X_TOPOLOGY(a) (0xC80 | (a) << 16)
#define NIX_AF_TL2X_PARENT(a) (0xE88 | (a) << 16)
#define NIX_AF_TL2X_SCHEDULE(a) (0xE00 | (a) << 16)
#define NIX_AF_TL2X_TOPOLOGY(a) (0xE80 | (a) << 16)
#define NIX_AF_TL2X_CIR(a) (0xE20 | (a) << 16)
#define NIX_AF_TL2X_PIR(a) (0xE30 | (a) << 16)
#define NIX_AF_TL3X_PARENT(a) (0x1088 | (a) << 16)
#define NIX_AF_TL3X_SCHEDULE(a) (0x1000 | (a) << 16)
#define NIX_AF_TL3X_SHAPE(a) (0x1010 | (a) << 16)
#define NIX_AF_TL3X_CIR(a) (0x1020 | (a) << 16)
#define NIX_AF_TL3X_PIR(a) (0x1030 | (a) << 16)
#define NIX_AF_TL3X_TOPOLOGY(a) (0x1080 | (a) << 16)
#define NIX_AF_TL4X_PARENT(a) (0x1288 | (a) << 16)
#define NIX_AF_TL4X_SCHEDULE(a) (0x1200 | (a) << 16)
#define NIX_AF_TL4X_SHAPE(a) (0x1210 | (a) << 16)
#define NIX_AF_TL4X_CIR(a) (0x1220 | (a) << 16)
#define NIX_AF_TL4X_PIR(a) (0x1230 | (a) << 16)
#define NIX_AF_TL4X_TOPOLOGY(a) (0x1280 | (a) << 16)
#define NIX_AF_MDQX_SCHEDULE(a) (0x1400 | (a) << 16)
#define NIX_AF_MDQX_SHAPE(a) (0x1410 | (a) << 16)
#define NIX_AF_MDQX_CIR(a) (0x1420 | (a) << 16)
#define NIX_AF_MDQX_PIR(a) (0x1430 | (a) << 16)
#define NIX_AF_MDQX_PARENT(a) (0x1480 | (a) << 16)
#define NIX_AF_TL3_TL2X_LINKX_CFG(a, b) (0x1700 | (a) << 16 | (b) << 3)

View file

@ -19,25 +19,11 @@
#include "cn10k.h"
#include "otx2_common.h"
/* Egress rate limiting definitions */
#define MAX_BURST_EXPONENT 0x0FULL
#define MAX_BURST_MANTISSA 0xFFULL
#define MAX_BURST_SIZE 130816ULL
#define MAX_RATE_DIVIDER_EXPONENT 12ULL
#define MAX_RATE_EXPONENT 0x0FULL
#define MAX_RATE_MANTISSA 0xFFULL
#include "qos.h"
#define CN10K_MAX_BURST_MANTISSA 0x7FFFULL
#define CN10K_MAX_BURST_SIZE 8453888ULL
/* Bitfields in NIX_TLX_PIR register */
#define TLX_RATE_MANTISSA GENMASK_ULL(8, 1)
#define TLX_RATE_EXPONENT GENMASK_ULL(12, 9)
#define TLX_RATE_DIVIDER_EXPONENT GENMASK_ULL(16, 13)
#define TLX_BURST_MANTISSA GENMASK_ULL(36, 29)
#define TLX_BURST_EXPONENT GENMASK_ULL(40, 37)
#define CN10K_TLX_BURST_MANTISSA GENMASK_ULL(43, 29)
#define CN10K_TLX_BURST_EXPONENT GENMASK_ULL(47, 44)
@ -147,8 +133,8 @@ static void otx2_get_egress_rate_cfg(u64 maxrate, u32 *exp,
}
}
static u64 otx2_get_txschq_rate_regval(struct otx2_nic *nic,
u64 maxrate, u32 burst)
u64 otx2_get_txschq_rate_regval(struct otx2_nic *nic,
u64 maxrate, u32 burst)
{
u32 burst_exp, burst_mantissa;
u32 exp, mantissa, div_exp;
@ -264,7 +250,6 @@ static int otx2_tc_egress_matchall_install(struct otx2_nic *nic,
struct netlink_ext_ack *extack = cls->common.extack;
struct flow_action *actions = &cls->rule->action;
struct flow_action_entry *entry;
u64 rate;
int err;
err = otx2_tc_validate_flow(nic, actions, extack);
@ -288,10 +273,8 @@ static int otx2_tc_egress_matchall_install(struct otx2_nic *nic,
NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second");
return -EOPNOTSUPP;
}
/* Convert bytes per second to Mbps */
rate = entry->police.rate_bytes_ps * 8;
rate = max_t(u64, rate / 1000000, 1);
err = otx2_set_matchall_egress_rate(nic, entry->police.burst, rate);
err = otx2_set_matchall_egress_rate(nic, entry->police.burst,
otx2_convert_rate(entry->police.rate_bytes_ps));
if (err)
return err;
nic->flags |= OTX2_FLAG_TC_MATCHALL_EGRESS_ENABLED;
@ -1127,6 +1110,8 @@ int otx2_setup_tc(struct net_device *netdev, enum tc_setup_type type,
switch (type) {
case TC_SETUP_BLOCK:
return otx2_setup_tc_block(netdev, type_data);
case TC_SETUP_QDISC_HTB:
return otx2_setup_tc_htb(netdev, type_data);
default:
return -EOPNOTSUPP;
}

View file

@ -464,12 +464,13 @@ static int otx2_tx_napi_handler(struct otx2_nic *pfvf,
break;
}
if (cq->cq_type == CQ_XDP) {
qidx = cq->cq_idx - pfvf->hw.rx_queues;
if (cq->cq_type == CQ_XDP)
otx2_xdp_snd_pkt_handler(pfvf, sq, cqe);
} else {
otx2_snd_pkt_handler(pfvf, cq, sq, cqe, budget,
&tx_pkts, &tx_bytes);
}
else
otx2_snd_pkt_handler(pfvf, cq, &pfvf->qset.sq[qidx],
cqe, budget, &tx_pkts, &tx_bytes);
cqe->hdr.cqe_type = NIX_XQE_TYPE_INVALID;
processed_cqe++;
@ -486,7 +487,11 @@ static int otx2_tx_napi_handler(struct otx2_nic *pfvf,
if (likely(tx_pkts)) {
struct netdev_queue *txq;
txq = netdev_get_tx_queue(pfvf->netdev, cq->cint_idx);
qidx = cq->cq_idx - pfvf->hw.rx_queues;
if (qidx >= pfvf->hw.tx_queues)
qidx -= pfvf->hw.xdp_queues;
txq = netdev_get_tx_queue(pfvf->netdev, qidx);
netdev_tx_completed_queue(txq, tx_pkts, tx_bytes);
/* Check if queue was stopped earlier due to ring full */
smp_mb();
@ -736,7 +741,8 @@ static void otx2_sqe_add_hdr(struct otx2_nic *pfvf, struct otx2_snd_queue *sq,
sqe_hdr->aura = sq->aura_id;
/* Post a CQE Tx after pkt transmission */
sqe_hdr->pnc = 1;
sqe_hdr->sq = qidx;
sqe_hdr->sq = (qidx >= pfvf->hw.tx_queues) ?
qidx + pfvf->hw.xdp_queues : qidx;
}
sqe_hdr->total = skb->len;
/* Set SQE identifier which will be used later for freeing SKB */
@ -1221,8 +1227,10 @@ void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq)
struct nix_cqe_tx_s *cqe;
int processed_cqe = 0;
struct sg_list *sg;
int qidx;
sq = &pfvf->qset.sq[cq->cint_idx];
qidx = cq->cq_idx - pfvf->hw.rx_queues;
sq = &pfvf->qset.sq[qidx];
if (otx2_nix_cq_op_status(pfvf, cq) || !cq->pend_cqe)
return;

View file

@ -102,7 +102,8 @@ enum cq_type {
CQ_RX,
CQ_TX,
CQ_XDP,
CQS_PER_CINT = 3, /* RQ + SQ + XDP */
CQ_QOS,
CQS_PER_CINT = 4, /* RQ + SQ + XDP + QOS_SQ */
};
struct otx2_cq_poll {

View file

@ -70,10 +70,6 @@ static void otx2vf_process_vfaf_mbox_msg(struct otx2_nic *vf,
case MBOX_MSG_NIX_LF_ALLOC:
mbox_handler_nix_lf_alloc(vf, (struct nix_lf_alloc_rsp *)msg);
break;
case MBOX_MSG_NIX_TXSCH_ALLOC:
mbox_handler_nix_txsch_alloc(vf,
(struct nix_txsch_alloc_rsp *)msg);
break;
case MBOX_MSG_NIX_BP_ENABLE:
mbox_handler_nix_bp_enable(vf, (struct nix_bp_cfg_rsp *)msg);
break;
@ -479,6 +475,7 @@ static const struct net_device_ops otx2vf_netdev_ops = {
.ndo_open = otx2vf_open,
.ndo_stop = otx2vf_stop,
.ndo_start_xmit = otx2vf_xmit,
.ndo_select_queue = otx2_select_queue,
.ndo_set_rx_mode = otx2vf_set_rx_mode,
.ndo_set_mac_address = otx2_set_mac_address,
.ndo_change_mtu = otx2vf_change_mtu,
@ -524,10 +521,10 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int num_vec = pci_msix_vec_count(pdev);
struct device *dev = &pdev->dev;
int err, qcount, qos_txqs;
struct net_device *netdev;
struct otx2_nic *vf;
struct otx2_hw *hw;
int err, qcount;
err = pcim_enable_device(pdev);
if (err) {
@ -550,7 +547,8 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_master(pdev);
qcount = num_online_cpus();
netdev = alloc_etherdev_mqs(sizeof(*vf), qcount, qcount);
qos_txqs = min_t(int, qcount, OTX2_QOS_MAX_LEAF_NODES);
netdev = alloc_etherdev_mqs(sizeof(*vf), qcount + qos_txqs, qcount);
if (!netdev) {
err = -ENOMEM;
goto err_release_regions;
@ -570,7 +568,7 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
hw->rx_queues = qcount;
hw->tx_queues = qcount;
hw->max_queues = qcount;
hw->tot_tx_queues = qcount;
hw->non_qos_queues = qcount;
hw->rbuf_len = OTX2_DEFAULT_RBUF_LEN;
/* Use CQE of 128 byte descriptor size by default */
hw->xqe_size = 128;
@ -699,6 +697,7 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (err)
goto err_shutdown_tc;
#endif
otx2_qos_init(vf, qos_txqs);
return 0;
@ -761,6 +760,7 @@ static void otx2vf_remove(struct pci_dev *pdev)
otx2_ptp_destroy(vf);
otx2_mcam_flow_del(vf);
otx2_shutdown_tc(vf);
otx2_shutdown_qos(vf);
otx2vf_disable_mbox_intr(vf);
otx2_detach_resources(&vf->mbox);
free_percpu(vf->hw.lmt_info);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2023 Marvell.
*
*/
#ifndef OTX2_QOS_H
#define OTX2_QOS_H
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/rhashtable.h>
#define OTX2_QOS_MAX_LVL 4
#define OTX2_QOS_MAX_PRIO 7
#define OTX2_QOS_MAX_LEAF_NODES 16
enum qos_smq_operations {
QOS_CFG_SQ,
QOS_SMQ_FLUSH,
};
u64 otx2_get_txschq_rate_regval(struct otx2_nic *nic, u64 maxrate, u32 burst);
int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb);
int otx2_qos_get_qid(struct otx2_nic *pfvf);
void otx2_qos_free_qid(struct otx2_nic *pfvf, int qidx);
int otx2_qos_enable_sq(struct otx2_nic *pfvf, int qidx);
void otx2_qos_disable_sq(struct otx2_nic *pfvf, int qidx);
struct otx2_qos_cfg {
u16 schq[NIX_TXSCH_LVL_CNT];
u16 schq_contig[NIX_TXSCH_LVL_CNT];
int static_node_pos[NIX_TXSCH_LVL_CNT];
int dwrr_node_pos[NIX_TXSCH_LVL_CNT];
u16 schq_contig_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC];
u16 schq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC];
};
struct otx2_qos {
DECLARE_HASHTABLE(qos_hlist, order_base_2(OTX2_QOS_MAX_LEAF_NODES));
struct mutex qos_lock; /* child list lock */
u16 qid_to_sqmap[OTX2_QOS_MAX_LEAF_NODES];
struct list_head qos_tree;
DECLARE_BITMAP(qos_sq_bmap, OTX2_QOS_MAX_LEAF_NODES);
u16 maj_id;
u16 defcls;
u8 link_cfg_lvl; /* LINKX_CFG CSRs mapped to TL3 or TL2's index ? */
};
struct otx2_qos_node {
struct list_head list; /* list management */
struct list_head child_list;
struct list_head child_schq_list;
struct hlist_node hlist;
DECLARE_BITMAP(prio_bmap, OTX2_QOS_MAX_PRIO + 1);
struct otx2_qos_node *parent; /* parent qos node */
u64 rate; /* htb params */
u64 ceil;
u32 classid;
u32 prio;
u16 schq; /* hw txschq */
u16 qid;
u16 prio_anchor;
u8 level;
};
#endif

View file

@ -0,0 +1,296 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Physical Function ethernet driver
*
* Copyright (C) 2023 Marvell.
*
*/
#include <linux/netdevice.h>
#include <net/tso.h>
#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);
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);
}

View file

@ -379,6 +379,12 @@ int mlx5e_htb_setup_tc(struct mlx5e_priv *priv, struct tc_htb_qopt_offload *htb_
if (!htb && htb_qopt->command != TC_HTB_CREATE)
return -EINVAL;
if (htb_qopt->prio) {
NL_SET_ERR_MSG_MOD(htb_qopt->extack,
"prio parameter is not supported by device with HTB offload enabled.");
return -EOPNOTSUPP;
}
switch (htb_qopt->command) {
case TC_HTB_CREATE:
if (!mlx5_qos_is_supported(priv->mdev)) {
@ -515,4 +521,3 @@ int mlx5e_mqprio_rl_get_node_hw_id(struct mlx5e_mqprio_rl *rl, int tc, u32 *hw_i
*hw_id = rl->leaves_id[tc];
return 0;
}

View file

@ -868,6 +868,7 @@ struct tc_htb_qopt_offload {
u16 qid;
u64 rate;
u64 ceil;
u8 prio;
};
#define TC_HTB_CLASSID_ROOT U32_MAX

View file

@ -1814,10 +1814,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
NL_SET_ERR_MSG(extack, "HTB offload doesn't support the quantum parameter");
goto failure;
}
if (hopt->prio) {
NL_SET_ERR_MSG(extack, "HTB offload doesn't support the prio parameter");
goto failure;
}
}
/* Keeping backward compatible with rate_table based iproute2 tc */
@ -1913,6 +1909,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
TC_HTB_CLASSID_ROOT,
.rate = max_t(u64, hopt->rate.rate, rate64),
.ceil = max_t(u64, hopt->ceil.rate, ceil64),
.prio = hopt->prio,
.extack = extack,
};
err = htb_offload(dev, &offload_opt);
@ -1933,6 +1930,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
TC_H_MIN(parent->common.classid),
.rate = max_t(u64, hopt->rate.rate, rate64),
.ceil = max_t(u64, hopt->ceil.rate, ceil64),
.prio = hopt->prio,
.extack = extack,
};
err = htb_offload(dev, &offload_opt);
@ -2018,6 +2016,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
.classid = cl->common.classid,
.rate = max_t(u64, hopt->rate.rate, rate64),
.ceil = max_t(u64, hopt->ceil.rate, ceil64),
.prio = hopt->prio,
.extack = extack,
};
err = htb_offload(dev, &offload_opt);