[PATCH net-next v3 2/3] net: stmmac: add tc-mqprio qdisc offload
From: Lorenzo Bianconi <hidden>
Date: 2026-08-08 14:50:32
Also in:
linux-arm-kernel
Subsystem:
networking drivers, stmmac ethernet driver, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Maxime Chevallier, Linus Torvalds
Implement offload of the tc-mqprio qdisc in the stmmac driver. The MTL TX scheduler is switched to strict priority, and the PSTQX/PSTC priority bitmask of each TX queue is programmed from the set of frame priorities mapped to the owning traffic class (qopt->prio_tc_map). The offload requires the DCB feature and a 1:1 TC to TX queue mapping, with the queue ranges bounded by the number of queues the MAC implements. Configurations with AVB queues are rejected, since forcing strict priority conflicts with the CBS algorithm. The per-queue priority bitmasks are stored in the unified qdisc state (priv->qdisc.prio) so they can be reprogrammed on device reopen, and are applied through the MTL TX queue priority map registers. Export stmmac_mac_config_tx_queues_prio() so the mqprio path can reprogram the queue priorities, leaving queues without snps,priority untouched. Destroying the mqprio qdisc, or failing its setup, restores the devicetree configured scheduling algorithm, TX queue priorities and TX queue weights, unless a different offload is active. Reviewed-by: Davide Caratti <redacted> Signed-off-by: Lorenzo Bianconi <redacted> --- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 15 ++++--- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 55 ++++++++++++++++++++++- 3 files changed, 65 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 33c37f843336..1967279a4ccc 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h@@ -307,6 +307,7 @@ struct stmmac_priv { u8 bands; u32 handle; u32 quanta[MTL_MAX_TX_QUEUES]; + u32 prio[MTL_MAX_TX_QUEUES]; } qdisc; struct dma_features dma_cap; struct stmmac_counters mmc;
@@ -402,6 +403,7 @@ enum stmmac_state { extern const struct dev_pm_ops stmmac_simple_pm_ops; void stmmac_set_tx_queue_weight(struct stmmac_priv *priv); +void stmmac_mac_config_tx_queues_prio(struct stmmac_priv *priv); int stmmac_mdio_unregister(struct net_device *ndev); int stmmac_mdio_register(struct net_device *ndev); int stmmac_mdio_reset(struct mii_bus *mii);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 5acddc9cbd3f..e19f75d8c393 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c@@ -3507,18 +3507,23 @@ static void stmmac_mac_config_rx_queues_prio(struct stmmac_priv *priv) * @priv: driver private structure * Description: It is used for configuring the TX Queue Priority */ -static void stmmac_mac_config_tx_queues_prio(struct stmmac_priv *priv) +void stmmac_mac_config_tx_queues_prio(struct stmmac_priv *priv) { u8 tx_queues_count = priv->plat->tx_queues_to_use; u8 queue; - u32 prio; for (queue = 0; queue < tx_queues_count; queue++) { - if (!priv->plat->tx_queues_cfg[queue].use_prio) + if (priv->qdisc.enable && + priv->qdisc.algo == MTL_TX_ALGORITHM_SP) { + stmmac_tx_queue_prio(priv, priv->hw, + priv->qdisc.prio[queue], queue); continue; + } - prio = priv->plat->tx_queues_cfg[queue].prio; - stmmac_tx_queue_prio(priv, priv->hw, prio, queue); + if (priv->plat->tx_queues_cfg[queue].use_prio) + stmmac_tx_queue_prio(priv, priv->hw, + priv->plat->tx_queues_cfg[queue].prio, + queue); } }
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index 15949b5d9f32..336350d6786b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c@@ -1219,6 +1219,7 @@ static void stmmac_qdisc_restore_dt_config(struct stmmac_priv *priv) priv->qdisc.enable = false; netif_set_real_num_tx_queues(priv->dev, priv->plat->tx_queues_to_use); stmmac_set_tx_queue_weight(priv); + stmmac_mac_config_tx_queues_prio(priv); stmmac_prog_mtl_tx_algorithms(priv, priv->hw, priv->plat->tx_sched_algorithm); }
@@ -1345,8 +1346,16 @@ static void stmmac_reset_tc_mqprio(struct net_device *ndev, struct stmmac_priv *priv = netdev_priv(ndev); netdev_reset_tc(ndev); - netif_set_real_num_tx_queues(ndev, priv->plat->tx_queues_to_use); stmmac_fpe_map_preemption_class(priv, ndev, extack, 0); + + if (priv->qdisc.enable && priv->qdisc.algo == MTL_TX_ALGORITHM_SP) { + stmmac_qdisc_restore_dt_config(priv); + } else { + u8 bands = priv->qdisc.enable ? priv->qdisc.bands + : priv->plat->tx_queues_to_use; + + netif_set_real_num_tx_queues(ndev, bands); + } } static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv,
@@ -1357,13 +1366,24 @@ static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv, u32 offset, count, num_stack_tx_queues = 0; struct net_device *ndev = priv->dev; u32 num_tc = qopt->num_tc; - int err; + int i, tc, err; if (!num_tc) { stmmac_reset_tc_mqprio(ndev, extack); return 0; } + if (!priv->dma_cap.dcben) + return -EOPNOTSUPP; + + /* Forcing strict priority conflicts with the CBS algorithm of AVB + * queues, so reject the offload when any queue is configured as AVB. + */ + for (i = 0; i < priv->plat->tx_queues_to_use; i++) { + if (priv->plat->tx_queues_cfg[i].mode_to_use == MTL_QUEUE_AVB) + return -EOPNOTSUPP; + } + err = netdev_set_num_tc(ndev, num_tc); if (err) return err;
@@ -1373,6 +1393,15 @@ static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv, count = qopt->count[tc]; num_stack_tx_queues += count; + if (offset + count > priv->plat->tx_queues_to_use) + goto err_reset_tc; + + /* The offload switches the MTL scheduler to strict priority, + * which only supports a 1:1 TC to TX queue mapping. + */ + if (count > 1) + goto err_reset_tc; + err = netdev_set_tc_queue(ndev, tc, count, offset); if (err) goto err_reset_tc;
@@ -1387,6 +1416,28 @@ static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv, if (err) goto err_reset_tc; + for (i = 0; i < priv->plat->tx_queues_to_use; i++) { + u32 prio = 0; + + for (tc = 0; tc < num_tc; tc++) { + int p; + + if (qopt->offset[tc] != i) + continue; + + for (p = 0; p < ARRAY_SIZE(qopt->prio_tc_map); p++) + if (qopt->prio_tc_map[p] == tc) + prio |= BIT(p); + break; + } + + priv->qdisc.prio[i] = prio; + stmmac_tx_queue_prio(priv, priv->hw, prio, i); + } + stmmac_prog_mtl_tx_algorithms(priv, priv->hw, MTL_TX_ALGORITHM_SP); + priv->qdisc.algo = MTL_TX_ALGORITHM_SP; + priv->qdisc.enable = true; + return 0; err_reset_tc:
--
2.55.0