[PATCH v5 net-next] octeontx2-pf: add mqprio bandwidth offload for NIX TX schedulers
From: Ratheesh Kannoth <rkannoth@marvell.com>
Date: 2026-08-06 09:54:59
Also in:
lkml
Subsystem:
marvell octeontx2 physical function driver, marvell octeontx2 rvu admin function driver, networking drivers, the rest · Maintainers:
Sunil Goutham, Geetha sowjanya, Ratheesh Kannoth, Subbaraya Sundeep, Bharat Bhushan, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Add TC_SETUP_QDISC_MQPRIO handling to program per-queue MDQ CIR/PIR shaping after mqprio channel mode setup, widen SMQ allocation when per-queue rate limiting is enabled, and align MDQ parents under that mode. Reuse otx2_convert_rate for bps-to-Mbps, fix sparse register writes when only one of min/max is set, and improve AF txschq map mismatch diagnostics. Configuration examples: 1) Set up commands a. ifconfig eth0 xx.xx.xx.xx up b. tc qdisc add dev eth0 parent root handle 100 mqprio num_tc 4 \ map 0 1 2 3 queues 1@0 1@1 1@2 1@3 hw 1 mode channel shaper \ bw_rlimit \ min_rate 100Mbit 200Mbit 300Mbit 400Mbit \ max_rate 150Mbit 250Mbit 350Mbit 450Mbit c. iptables -t mangle -A POSTROUTING -p udp -j CLASSIFY --set-class 0:1 This will guarantee max rate as 250Mbits for UDP traffic 2) Tear down mqprio a. ifconfig eth0 up b. tc qdisc del dev eth0 root Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com> --- v4 -> v5: Addressed sashiko comments https://sashiko.dev/#/patchset/20260803042724.3380209-1-rkannoth%40marvell.com v3 -> v4: Addressed sashiko comments https://lore.kernel.org/netdev/20260729105139.2302908-1-rkannoth@marvell.com/ (local) v2 -> v3: Addressed sashiko comments https://lore.kernel.org/netdev/amnYX866mYx02cBe@rkannoth-OptiPlex-7090/T/#m67310cbec48b21c7720858ab3a1ea083a0f8dc10 (local) v1 -> v2: Addressed sashiko comments https://lore.kernel.org/netdev/20260724075010.2665758-1-rkannoth@marvell.com/ (local) --- .../ethernet/marvell/octeontx2/af/rvu_nix.c | 6 +- .../marvell/octeontx2/nic/otx2_common.c | 152 +++++++- .../marvell/octeontx2/nic/otx2_common.h | 10 + .../marvell/octeontx2/nic/otx2_dcbnl.c | 6 + .../marvell/octeontx2/nic/otx2_ethtool.c | 11 + .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 5 + .../ethernet/marvell/octeontx2/nic/otx2_tc.c | 331 ++++++++++++++++++ .../net/ethernet/marvell/octeontx2/nic/qos.c | 15 + .../net/ethernet/marvell/octeontx2/nic/qos.h | 1 + 9 files changed, 535 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index b81c47ea023b..22f64c1ede91 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c@@ -331,8 +331,12 @@ static bool is_valid_txschq(struct rvu *rvu, int blkaddr, return true; } - if (map_func != pcifunc) + if (map_func != pcifunc) { + dev_err_ratelimited(rvu->dev, + "pcifunc %x map pcifunc %x not equal, lvl=%u schq=%u\n", + pcifunc, map_func, lvl, schq); return false; + } return true; }
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
index ca73a94db794..b4889ef8fab9 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c@@ -614,6 +614,149 @@ void otx2_get_mac_from_af(struct net_device *netdev) } EXPORT_SYMBOL(otx2_get_mac_from_af); +static int +otx2_nix_tmq_reg_write(struct otx2_nic *pfvf, int cnt, + u64 reg_addr[MAX_REGS_PER_MBOX_MSG], + u64 reg_val[MAX_REGS_PER_MBOX_MSG]) +{ + struct mbox *mbox = &pfvf->mbox; + struct nix_txschq_config *req; + int i, err; + + mutex_lock(&mbox->lock); + req = otx2_mbox_alloc_msg_nix_txschq_cfg(mbox); + if (!req) { + mutex_unlock(&mbox->lock); + return -ENOMEM; + } + + req->lvl = NIX_TXSCH_LVL_MDQ; + req->num_regs = cnt; + + for (i = 0; i < cnt; i++) { + req->reg[i] = reg_addr[i]; + req->regval[i] = reg_val[i]; + } + + err = otx2_sync_mbox_msg(mbox); + mutex_unlock(&mbox->lock); + + return err; +} + +int otx2_nix_tm_clear_queue_shaper(struct otx2_nic *pfvf) +{ + u64 reg_addr[MAX_REGS_PER_MBOX_MSG]; + u64 reg_val[MAX_REGS_PER_MBOX_MSG]; + DECLARE_BITMAP(slots, 2048); + int err, smq, q, cnt = 0; + + bitmap_zero(slots, 2048); + + for (q = 0; q < pfvf->hw.non_qos_queues; q++) { + smq = otx2_get_smq_idx(pfvf, q); + if (test_bit(smq, slots)) + continue; + + reg_addr[cnt] = NIX_AF_MDQX_PIR(smq); + reg_val[cnt] = 0; + cnt++; + + reg_addr[cnt] = NIX_AF_MDQX_CIR(smq); + reg_val[cnt] = 0; + cnt++; + + set_bit(smq, slots); + + if (cnt < MAX_REGS_PER_MBOX_MSG - 1) + continue; + + err = otx2_nix_tmq_reg_write(pfvf, cnt, + reg_addr, reg_val); + if (err) + goto fail; + cnt = 0; + } + + if (cnt) { + err = otx2_nix_tmq_reg_write(pfvf, cnt, + reg_addr, reg_val); + if (err) + goto fail; + } + + return 0; +fail: + return err; +} + +int otx2_nix_tm_set_queue_shaper(struct otx2_nic *pfvf, + int txq, u64 minrate, u64 maxrate) +{ + struct mbox *mbox = &pfvf->mbox; + struct nix_txschq_config *req; + int err, smq, n = 0; + u64 reg_addr[2]; + u64 reg_val[2]; + u64 rate; + + if (!maxrate && !minrate) { + smq = otx2_get_smq_idx(pfvf, txq); + reg_addr[0] = NIX_AF_MDQX_PIR(smq); + reg_val[0] = 0; + reg_addr[1] = NIX_AF_MDQX_CIR(smq); + reg_val[1] = 0; + return otx2_nix_tmq_reg_write(pfvf, 2, reg_addr, reg_val); + } + + smq = otx2_get_smq_idx(pfvf, txq); + + mutex_lock(&mbox->lock); + req = otx2_mbox_alloc_msg_nix_txschq_cfg(mbox); + if (!req) { + mutex_unlock(&mbox->lock); + return -ENOMEM; + } + + req->lvl = NIX_TXSCH_LVL_MDQ; + + /* MQPRIO exposes only min/max rate, not burst. Pass burst 0 so + * otx2_get_egress_burst_cfg() programmes the largest burst the NIX + * encoding supports (CN10K_MAX_BURST_SIZE on CN10K). This differs + * from the 65536 byte default used in the HTB path, which is a + * kernel-side default when no explicit burst is configured, not a + * hardware cap. + * + * Sparse MDQ writes: PIR and CIR are programmed independently. If + * maxrate or minrate is zero, the matching register is not written. + * When the tc mqprio command omits min_rate or max_rate (or sets a + * per-TC rate to zero), the driver leaves the other MDQ shaper + * register untouched; any rate previously applied there remains in + * hardware until both inputs are zero (full clear) or the register + * is written again. Users reconfiguring shaping must supply both + * parameters or delete mqprio before applying a new rate profile. + */ + if (maxrate) { + req->reg[n] = NIX_AF_MDQX_PIR(smq); + rate = otx2_convert_rate(maxrate); + req->regval[n] = otx2_get_txschq_rate_regval(pfvf, rate, 0); + n++; + } + + /* CIR+PIR support is required and checked at mqprio setup. */ + if (minrate) { + req->reg[n] = NIX_AF_MDQX_CIR(smq); + rate = otx2_convert_rate(minrate); + req->regval[n] = otx2_get_txschq_rate_regval(pfvf, rate, 0); + n++; + } + req->num_regs = n; + + err = otx2_sync_mbox_msg(mbox); + mutex_unlock(&mbox->lock); + return err; +} + int otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool txschq_for_pfc) { u16 (*schq_list)[MAX_TXSCHQ_PER_FUNC];
@@ -650,7 +793,11 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool txschq_for (u64)hw->smq_link_type); req->num_regs++; /* MDQ config */ - parent = schq_list[NIX_TXSCH_LVL_TL4][prio]; + if (pfvf->flags & OTX2_FLAG_PER_Q_RATE_LIMIT_ENABLED) + parent = schq_list[NIX_TXSCH_LVL_TL4][0]; + else + parent = schq_list[NIX_TXSCH_LVL_TL4][prio]; + req->reg[1] = NIX_AF_MDQX_PARENT(schq); req->regval[1] = parent << 16; req->num_regs++;
@@ -778,6 +925,9 @@ int otx2_txsch_alloc(struct otx2_nic *pfvf) req->schq[NIX_TXSCH_LVL_TL4] = chan_cnt; } + if (pfvf->flags & OTX2_FLAG_PER_Q_RATE_LIMIT_ENABLED) + req->schq[NIX_TXSCH_LVL_SMQ] = pfvf->hw.non_qos_queues; + rc = otx2_sync_mbox_msg(&pfvf->mbox); if (rc) return rc;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
index eecee612b7b2..df175fb14951 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h@@ -512,9 +512,14 @@ struct otx2_nic { #define OTX2_FLAG_REP_MODE_ENABLED BIT_ULL(18) #define OTX2_FLAG_PORT_UP BIT_ULL(19) #define OTX2_FLAG_IPSEC_OFFLOAD_ENABLED BIT_ULL(20) +#define OTX2_FLAG_PER_Q_RATE_LIMIT_ENABLED BIT_ULL(21) u64 flags; u64 *cq_op_addr; + u32 mqprio_flags; + u64 *mqprio_min_rate; + u64 *mqprio_max_rate; + struct bpf_prog *xdp_prog; struct otx2_qset qset; struct otx2_hw hw;
@@ -1246,6 +1251,11 @@ dma_addr_t otx2_dma_map_skb_frag(struct otx2_nic *pfvf, struct sk_buff *skb, int seg, int *len); void otx2_dma_unmap_skb_frags(struct otx2_nic *pfvf, struct sg_list *sg); int otx2_read_free_sqe(struct otx2_nic *pfvf, u16 qidx); +int otx2_nix_tm_set_queue_shaper(struct otx2_nic *pfvf, int txq, + u64 minrate, u64 maxrate); +int otx2_nix_tm_clear_queue_shaper(struct otx2_nic *pfvf); +int otx2_mqprio_down(struct otx2_nic *pfvf); +int otx2_mqprio_up(struct otx2_nic *pfvf); void otx2_queue_vf_work(struct mbox *mw, struct workqueue_struct *mbox_wq, int first, int mdevs, u64 intr); int otx2_del_mcam_flow_entry(struct otx2_nic *nic, u16 entry,
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c
index f110dfa42360..676f78d8fb34 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c@@ -413,6 +413,12 @@ static int otx2_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc) u8 old_pfc_en; int err; + if ((pfvf->flags & OTX2_FLAG_PER_Q_RATE_LIMIT_ENABLED) && pfc->pfc_en) { + netdev_err(dev, + "PFC: cannot enable while mqprio bandwidth offload is active\n"); + return -EOPNOTSUPP; + } + old_pfc_en = pfvf->pfc_en; pfvf->pfc_en = pfc->pfc_en;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
index a0340f3422bf..3f494cfbfcee 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c@@ -287,6 +287,17 @@ static int otx2_set_channels(struct net_device *dev, return -EINVAL; } + if ((pfvf->flags & OTX2_FLAG_PER_Q_RATE_LIMIT_ENABLED) && + (channel->tx_count != pfvf->hw.tx_queues || + channel->rx_count != pfvf->hw.rx_queues)) { + err = otx2_mqprio_down(pfvf); + if (err) + return err; + + netdev_info(dev, + "Removed mqprio bandwidth offload due to channel count change\n"); + } + if (if_up) dev->netdev_ops->ndo_stop(dev);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
index c995f2900859..a5e9908fa9b5 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c@@ -1980,6 +1980,10 @@ int otx2_open(struct net_device *netdev) if (err) goto err_free_mem; + err = otx2_mqprio_up(pf); + if (err) + goto err_free_hw; + /* Register NAPI handler */ for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { cq_poll = &qset->napi[qidx];
@@ -2140,6 +2144,7 @@ int otx2_open(struct net_device *netdev) free_irq(vec, pf); err_disable_napi: otx2_disable_napi(pf); +err_free_hw: otx2_free_hw_resources(pf); err_free_mem: otx2_free_queue_mem(qset);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
index 0b46ec29e64e..f7d80d5980d7 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c@@ -16,6 +16,7 @@ #include <net/tc_act/tc_mirred.h> #include <net/tc_act/tc_vlan.h> #include <net/ipv6.h> +#include <net/pkt_sched.h> #include "cn10k.h" #include "otx2_common.h"
@@ -59,6 +60,9 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst, else *burst_mantissa = tmp / (1ULL << (*burst_exp - 7)); } else { + /* burst 0: largest encodable burst (CN10K_MAX_BURST_SIZE on + * CN10K), not a minimal burst. + */ *burst_exp = MAX_BURST_EXPONENT; *burst_mantissa = max_mantissa; }
@@ -1598,14 +1602,341 @@ static int otx2_setup_tc_block(struct net_device *netdev, nic, nic, ingress); } +/* Tear down mqprio bandwidth offload: clear per-queue shapers, TC + * mappings, and the rate-limit flag. Used on explicit mqprio teardown + * (tc qdisc del), not on internal stop/open cycles such as MTU change. + */ +static void otx2_mqprio_free_cache(struct otx2_nic *pfvf) +{ + devm_kfree(pfvf->dev, pfvf->mqprio_min_rate); + devm_kfree(pfvf->dev, pfvf->mqprio_max_rate); + pfvf->mqprio_min_rate = NULL; + pfvf->mqprio_max_rate = NULL; + pfvf->mqprio_flags = 0; +} + +static int otx2_mqprio_alloc_cache(struct otx2_nic *pfvf) +{ + u16 num_txq = pfvf->hw.non_qos_queues; + + otx2_mqprio_free_cache(pfvf); + + pfvf->mqprio_min_rate = devm_kcalloc(pfvf->dev, num_txq, + sizeof(*pfvf->mqprio_min_rate), + GFP_KERNEL); + pfvf->mqprio_max_rate = devm_kcalloc(pfvf->dev, num_txq, + sizeof(*pfvf->mqprio_max_rate), + GFP_KERNEL); + if (!pfvf->mqprio_min_rate || !pfvf->mqprio_max_rate) { + otx2_mqprio_free_cache(pfvf); + return -ENOMEM; + } + + return 0; +} + +static bool otx2_txschq_is_allocated(struct otx2_nic *pfvf) +{ + int lvl, idx; + + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + for (idx = 0; idx < pfvf->hw.txschq_cnt[lvl]; idx++) { + if (pfvf->hw.txschq_list[lvl][idx]) + return true; + } + } + + return false; +} + +int otx2_mqprio_down(struct otx2_nic *pfvf) +{ + struct net_device *netdev = pfvf->netdev; + int err = 0; + + if (!(pfvf->flags & OTX2_FLAG_PER_Q_RATE_LIMIT_ENABLED)) + return 0; + + if (otx2_txschq_is_allocated(pfvf)) + err = otx2_nix_tm_clear_queue_shaper(pfvf); + + pfvf->flags &= ~OTX2_FLAG_PER_Q_RATE_LIMIT_ENABLED; + netdev_set_num_tc(netdev, 0); + otx2_mqprio_free_cache(pfvf); + + return err; +} + +int otx2_mqprio_up(struct otx2_nic *pfvf) +{ + struct net_device *netdev = pfvf->netdev; + int txq, err; + + if (!(pfvf->flags & OTX2_FLAG_PER_Q_RATE_LIMIT_ENABLED)) + return 0; + + if (!pfvf->mqprio_min_rate || !pfvf->mqprio_max_rate) + return 0; + + for (txq = 0; txq < pfvf->hw.non_qos_queues; txq++) { + u64 min_rate = 0, max_rate = 0; + + if (pfvf->mqprio_flags & TC_MQPRIO_F_MIN_RATE) + min_rate = pfvf->mqprio_min_rate[txq]; + if (pfvf->mqprio_flags & TC_MQPRIO_F_MAX_RATE) + max_rate = pfvf->mqprio_max_rate[txq]; + + if (!min_rate && !max_rate) + continue; + + err = otx2_nix_tm_set_queue_shaper(pfvf, txq, min_rate, + max_rate); + if (err) { + netdev_err(netdev, + "mqprio: failed to restore shaper for txq %d: %d\n", + txq, err); + return err; + } + } + + return 0; +} + +/* Restart the netdev to reprogram the TX scheduler hierarchy for mqprio + * bandwidth offload. Both mqprio add and delete (when offload was active) + * take this path via ndo_stop()/ndo_open() so VF-specific open logic (e.g. + * LBK carrier on) runs correctly. The full stop/open cycle clears + * carrier, stops all TX queues, tears down IRQs/NAPI and drops in-flight + * traffic. If open fails, netif_close() clears IFF_UP and leaves the + * interface administratively down. + */ +static int otx2_mqprio_restart_netdev(struct net_device *netdev, bool rate_limit) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + const struct net_device_ops *ops = netdev->netdev_ops; + int err; + + netdev_info(netdev, + "mqprio: restarting interface to reprogram TX scheduler; in-flight traffic will be dropped\n"); + + err = ops->ndo_stop(netdev); + if (err) + return err; + + /* Set before ndo_open() so otx2_txsch_alloc() widens SMQ allocation. */ + if (rate_limit) + pfvf->flags |= OTX2_FLAG_PER_Q_RATE_LIMIT_ENABLED; + + err = ops->ndo_open(netdev); + if (err) { + int down_err; + + netdev_err(netdev, + "Failed to restart device after mqprio change: %d\n", + err); + down_err = otx2_mqprio_down(pfvf); + if (down_err) + netdev_err(netdev, + "mqprio: failed to clear shapers after restart error: %d\n", + down_err); + /* Caller holds netdev/RTNL lock; dev_close() would deadlock. */ + netif_close(netdev); + } + + return err; +} + +static u64 otx2_mqprio_per_queue_rate(u64 rate, u32 qcount) +{ + if (!rate || qcount <= 1) + return rate; + + return div_u64(rate, qcount); +} + +static int otx2_teardown_tc_mqprio(struct otx2_nic *pfvf, + struct tc_mqprio_qopt_offload *mqprio) +{ + bool had_mqprio = !!(pfvf->flags & OTX2_FLAG_PER_Q_RATE_LIMIT_ENABLED); + struct tc_mqprio_qopt *qopt = &mqprio->qopt; + struct net_device *netdev = pfvf->netdev; + bool if_up = netif_running(netdev); + int down_err, err; + + qopt->hw = 0; + + /* Skip the netdev restart when mqprio offload was not active. */ + if (!had_mqprio) + return 0; + + down_err = otx2_mqprio_down(pfvf); + + if (if_up) { + err = otx2_mqprio_restart_netdev(netdev, false); + if (err) + return err; + } + + return down_err; +} + +static int otx2_setup_tc_mqprio(struct net_device *netdev, + struct tc_mqprio_qopt_offload *mqprio) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct tc_mqprio_qopt *qopt = &mqprio->qopt; + bool if_up = netif_running(netdev); + int tc, txq, err, i; + + if (!if_up) { + netdev_err(netdev, "mqprio: setup requires interface UP\n"); + return -EOPNOTSUPP; + } + + if (!qopt->hw) + return otx2_teardown_tc_mqprio(pfvf, mqprio); + + if (mqprio->shaper != TC_MQPRIO_SHAPER_BW_RATE) { + netdev_err(netdev, "Unsupported mqprio shaper %#x\n", mqprio->shaper); + return -EOPNOTSUPP; + } + + if (!test_bit(QOS_CIR_PIR_SUPPORT, &pfvf->hw.cap_flag)) { + netdev_err(netdev, + "mqprio: bandwidth offload requires CIR+PIR support\n"); + return -EOPNOTSUPP; + } + + if (is_otx2_sdp_rep(pfvf->pdev)) { + netdev_err(netdev, "mqprio: bandwidth offload not supported on SDP rep\n"); + return -EOPNOTSUPP; + } + + if (otx2_qos_htb_active(pfvf)) { + netdev_err(netdev, "mqprio: cannot enable offload while HTB is active\n"); + return -EOPNOTSUPP; + } + + if (pfvf->pfc_en) { + netdev_err(netdev, + "mqprio: cannot enable offload while PFC is enabled\n"); + return -EOPNOTSUPP; + } + + if (qopt->num_tc > pfvf->hw.non_qos_queues) { + netdev_err(netdev, "Number of TCs (%u) exceeds hw queues %u\n", + qopt->num_tc, pfvf->hw.non_qos_queues); + return -EINVAL; + } + + if (pfvf->hw.non_qos_queues > MAX_TXSCHQ_PER_FUNC) { + netdev_err(netdev, + "Number of queues (%u) exceeds max scheduler queues %u\n", + pfvf->hw.non_qos_queues, MAX_TXSCHQ_PER_FUNC); + return -EINVAL; + } + + for (tc = 0; tc < qopt->num_tc; tc++) { + u64 min_rate = 0, max_rate = 0; + + if (mqprio->flags & TC_MQPRIO_F_MIN_RATE) + min_rate = mqprio->min_rate[tc]; + if (mqprio->flags & TC_MQPRIO_F_MAX_RATE) + max_rate = mqprio->max_rate[tc]; + + if (min_rate && max_rate && min_rate > max_rate) { + netdev_err(netdev, + "min_rate %llu exceeds max_rate %llu for tc %d\n", + min_rate, max_rate, tc); + return -EINVAL; + } + + for (txq = qopt->offset[tc]; + txq < qopt->offset[tc] + qopt->count[tc]; txq++) { + if (txq >= netdev->real_num_tx_queues) + return -EINVAL; + } + } + + err = otx2_mqprio_restart_netdev(pfvf->netdev, true); + if (err) + return err; + + err = otx2_mqprio_alloc_cache(pfvf); + if (err) + goto cleanup; + + pfvf->mqprio_flags = mqprio->flags; + + for (tc = 0; tc < qopt->num_tc; tc++) { + u64 min_rate = 0, max_rate = 0; + u32 qcount = qopt->count[tc]; + + /* Rates omitted from tc mqprio are left at zero; see sparse + * MDQ write behaviour in otx2_nix_tm_set_queue_shaper(). + */ + if (mqprio->flags & TC_MQPRIO_F_MIN_RATE) + min_rate = otx2_mqprio_per_queue_rate(mqprio->min_rate[tc], + qcount); + if (mqprio->flags & TC_MQPRIO_F_MAX_RATE) + max_rate = otx2_mqprio_per_queue_rate(mqprio->max_rate[tc], + qcount); + + for (txq = qopt->offset[tc]; + txq < qopt->offset[tc] + qcount; txq++) { + netdev_dbg(netdev, + "mqprio: tc %d txq %d min_rate %llu max_rate %llu\n", + tc, txq, min_rate, max_rate); + + pfvf->mqprio_min_rate[txq] = min_rate; + pfvf->mqprio_max_rate[txq] = max_rate; + + err = otx2_nix_tm_set_queue_shaper(pfvf, txq, + min_rate, max_rate); + if (err) + goto cleanup; + } + } + + netdev_set_num_tc(netdev, qopt->num_tc); + for (i = 0; i < qopt->num_tc; i++) + netdev_set_tc_queue(netdev, i, qopt->count[i], qopt->offset[i]); + + qopt->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + return 0; + +cleanup: + otx2_teardown_tc_mqprio(pfvf, mqprio); + return err; +} + +static int otx2_setup_tc_query_caps(void *type_data) +{ + struct tc_query_caps_base *base = type_data; + struct tc_mqprio_caps *caps; + + if (base->type != TC_SETUP_QDISC_MQPRIO) + return -EOPNOTSUPP; + + caps = base->caps; + caps->validate_queue_counts = true; + + return 0; +} + int otx2_setup_tc(struct net_device *netdev, enum tc_setup_type type, void *type_data) { switch (type) { + case TC_QUERY_CAPS: + return otx2_setup_tc_query_caps(type_data); 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); + case TC_SETUP_QDISC_MQPRIO: + return otx2_setup_tc_mqprio(netdev, type_data); default: return -EOPNOTSUPP; }
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos.c b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c
index 69c0911e28e9..0fa5c65d6221 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/qos.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c@@ -118,6 +118,9 @@ static void otx2_config_sched_shaping(struct otx2_nic *pfvf, /* configure PIR */ maxrate = (node->rate > node->ceil) ? node->rate : node->ceil; + /* 65536 is the kernel-side default burst when HTB does not supply an + * explicit value, not the NIX hardware maximum (CN10K_MAX_BURST_SIZE). + */ cfg->regval[*num_regs] = otx2_get_txschq_rate_regval(pfvf, maxrate, 65536); (*num_regs)++;
@@ -1729,11 +1732,23 @@ void otx2_qos_config_txschq(struct otx2_nic *pfvf) otx2_qos_root_destroy(pfvf); } +bool otx2_qos_htb_active(struct otx2_nic *pfvf) +{ + return otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID); +} + int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb) { struct otx2_nic *pfvf = netdev_priv(ndev); int res; + if ((pfvf->flags & OTX2_FLAG_PER_Q_RATE_LIMIT_ENABLED) && + htb->command != TC_HTB_DESTROY) { + NL_SET_ERR_MSG_MOD(htb->extack, + "HTB offload cannot be used with mqprio bandwidth offload active"); + return -EOPNOTSUPP; + } + switch (htb->command) { case TC_HTB_CREATE: return otx2_qos_root_add(pfvf, htb->parent_classid,
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos.h b/drivers/net/ethernet/marvell/octeontx2/nic/qos.h
index 221bd0438f60..c777cc42eac9 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/qos.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/qos.h@@ -23,6 +23,7 @@ enum qos_smq_operations { 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); +bool otx2_qos_htb_active(struct otx2_nic *pfvf); 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);
--
2.43.0