Implement offload of the tc-ets qdisc in the stmmac driver. The number of ETS bands cannot exceed the number of TX queues, and strict priority bands (zero quanta) are rejected since sched prio is not implemented in the ETS offload but will be implemented in the mqprio qdisc offload. Weighted bands whose quantum is zero are rejected as well, since MTL DWRR would never schedule traffic on them. Configurations with AVB queues, or on cores without the DCB feature, are not offloaded either. The requested quanta are applied to the TX queues and the MTL scheduler is switched to DWRR. The number of active TX queues is restricted to the band count so the stack never routes traffic onto queues left with a zero quantum, which the MTL scheduler would never drain. The offload state is cached in priv->qdisc and re-applied on device open (scheduler, queue weights and queue count), so it survives a reopen or a reset. TC_ETS_GRAFT is not offloaded and silently falls back to the software implementation, while TC_ETS_STATS is accepted when the ETS qdisc is offloaded, although no hardware statistics are provided. The DESTROY and STATS paths act only on the ETS instance actually offloaded, so a software-only child ETS or the old root qdisc of a replace cannot reset the hardware configuration of a live offload. The FPE preemption mapping is now validated against the effective scheduling algorithm and weights (the DWRR quanta when ETS is offloaded) rather than the DT configuration. Export stmmac_set_tx_queue_weight() so the ETS path can reprogram queue weights, and have it skip AVB queues, whose weight register is repurposed as the CBS idle slope. Wire TC_SETUP_QDISC_ETS into stmmac_setup_tc(). Destroying the ETS qdisc restores the devicetree configured scheduling algorithm and TX queue weights. Reviewed-by: Maxime Chevallier Reviewed-by: Davide Caratti Signed-off-by: Lorenzo Bianconi --- drivers/net/ethernet/stmicro/stmmac/hwif.h | 4 + drivers/net/ethernet/stmicro/stmmac/stmmac.h | 8 ++ drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c | 20 ++++- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 34 +++++-- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 104 ++++++++++++++++++++++ 5 files changed, 159 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 04dafec021b4..b70883a53b09 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -580,6 +580,8 @@ struct stmmac_tc_ops { struct tc_taprio_qopt_offload *qopt); int (*setup_etf)(struct stmmac_priv *priv, struct tc_etf_qopt_offload *qopt); + int (*setup_ets)(struct stmmac_priv *priv, + struct tc_ets_qopt_offload *qopt); int (*query_caps)(struct stmmac_priv *priv, struct tc_query_caps_base *base); int (*setup_mqprio)(struct stmmac_priv *priv, @@ -598,6 +600,8 @@ struct stmmac_tc_ops { stmmac_do_callback(__priv, tc, setup_taprio, __args) #define stmmac_tc_setup_etf(__priv, __args...) \ stmmac_do_callback(__priv, tc, setup_etf, __args) +#define stmmac_tc_setup_ets(__priv, __args...) \ + stmmac_do_callback(__priv, tc, setup_ets, __args) #define stmmac_tc_query_caps(__priv, __args...) \ stmmac_do_callback(__priv, tc, query_caps, __args) #define stmmac_tc_setup_mqprio(__priv, __args...) \ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 8ba8f03e1ce0..33c37f843336 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -301,6 +301,13 @@ struct stmmac_priv { /* Protect est parameters */ struct mutex est_lock; struct stmmac_est *est; + struct { + bool enable; + u8 algo; + u8 bands; + u32 handle; + u32 quanta[MTL_MAX_TX_QUEUES]; + } qdisc; struct dma_features dma_cap; struct stmmac_counters mmc; int hw_cap_support; @@ -394,6 +401,7 @@ enum stmmac_state { extern const struct dev_pm_ops stmmac_simple_pm_ops; +void stmmac_set_tx_queue_weight(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_fpe.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c index c54c70224351..d84b6fdc01b3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c @@ -205,7 +205,10 @@ int dwmac5_fpe_map_preemption_class(struct net_device *ndev, { u32 val, offset, count, queue_weight, preemptible_txqs = 0; struct stmmac_priv *priv = netdev_priv(ndev); + struct plat_stmmacenet_data *pdata = priv->plat; int num_tc = netdev_get_num_tc(ndev); + u32 sched_algo; + bool ets; if (!pclass) goto update_mapping; @@ -216,6 +219,11 @@ int dwmac5_fpe_map_preemption_class(struct net_device *ndev, * "The number of Tx DMA channels is equal to the number of Tx queues, * and is direct one-to-one mapping." */ + + sched_algo = priv->qdisc.enable ? priv->qdisc.algo + : pdata->tx_sched_algorithm; + ets = priv->qdisc.enable && sched_algo == MTL_TX_ALGORITHM_DWRR; + for (u32 tc = 0; tc < num_tc; tc++) { count = ndev->tc_to_txq[tc].count; offset = ndev->tc_to_txq[tc].offset; @@ -227,16 +235,20 @@ int dwmac5_fpe_map_preemption_class(struct net_device *ndev, if (count == 1) continue; - if (priv->plat->tx_sched_algorithm == MTL_TX_ALGORITHM_SP) { + if (sched_algo == MTL_TX_ALGORITHM_SP) { NL_SET_ERR_MSG_MOD(extack, ALG_ERR_MSG); return -EINVAL; } - queue_weight = priv->plat->tx_queues_cfg[offset].weight; + queue_weight = ets ? priv->qdisc.quanta[offset] + : pdata->tx_queues_cfg[offset].weight; for (u32 i = 1; i < count; i++) { - if (priv->plat->tx_queues_cfg[offset + i].weight != - queue_weight) { + u32 weight; + + weight = ets ? priv->qdisc.quanta[offset + i] + : pdata->tx_queues_cfg[offset + i].weight; + if (weight != queue_weight) { NL_SET_ERR_MSG_FMT_MOD(extack, WEIGHT_ERR_MSG, queue_weight, tc); return -EINVAL; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b2b7d0242dd3..5acddc9cbd3f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3419,14 +3419,22 @@ static void stmmac_set_rings_length(struct stmmac_priv *priv) * @priv: driver private structure * Description: It is used for setting TX queues weight */ -static void stmmac_set_tx_queue_weight(struct stmmac_priv *priv) +void stmmac_set_tx_queue_weight(struct stmmac_priv *priv) { - u8 tx_queues_count = priv->plat->tx_queues_to_use; + struct plat_stmmacenet_data *pdata = priv->plat; + u8 tx_queues_count = pdata->tx_queues_to_use; u32 weight; u8 queue; for (queue = 0; queue < tx_queues_count; queue++) { - weight = priv->plat->tx_queues_cfg[queue].weight; + if (pdata->tx_queues_cfg[queue].mode_to_use == MTL_QUEUE_AVB) + continue; + + if (priv->qdisc.enable && + priv->qdisc.algo == MTL_TX_ALGORITHM_DWRR) + weight = priv->qdisc.quanta[queue]; + else + weight = pdata->tx_queues_cfg[queue].weight; stmmac_set_mtl_tx_queue_weight(priv, priv->hw, weight, queue); } } @@ -3570,9 +3578,15 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) priv->plat->rx_sched_algorithm); /* Configure MTL TX algorithms */ - if (tx_queues_count > 1) - stmmac_prog_mtl_tx_algorithms(priv, priv->hw, - priv->plat->tx_sched_algorithm); + if (tx_queues_count > 1) { + u8 tx_sched_algo; + + if (priv->qdisc.enable) + tx_sched_algo = priv->qdisc.algo; + else + tx_sched_algo = priv->plat->tx_sched_algorithm; + stmmac_prog_mtl_tx_algorithms(priv, priv->hw, tx_sched_algo); + } /* Configure CBS in AVB TX queues */ if (tx_queues_count > 1) @@ -3738,7 +3752,11 @@ static int stmmac_hw_setup(struct net_device *dev) /* Configure real RX and TX queues */ netif_set_real_num_rx_queues(dev, priv->plat->rx_queues_to_use); - netif_set_real_num_tx_queues(dev, priv->plat->tx_queues_to_use); + if (priv->qdisc.enable && priv->qdisc.algo == MTL_TX_ALGORITHM_DWRR) + netif_set_real_num_tx_queues(dev, priv->qdisc.bands); + else + netif_set_real_num_tx_queues(dev, + priv->plat->tx_queues_to_use); /* Start the ball rolling... */ stmmac_start_all_dma(priv); @@ -6437,6 +6455,8 @@ static int stmmac_setup_tc(struct net_device *ndev, enum tc_setup_type type, return stmmac_tc_setup_taprio(priv, priv, type_data); case TC_SETUP_QDISC_ETF: return stmmac_tc_setup_etf(priv, priv, type_data); + case TC_SETUP_QDISC_ETS: + return stmmac_tc_setup_ets(priv, priv, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 14cabe76e53e..15949b5d9f32 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -1210,6 +1210,108 @@ static int tc_setup_etf(struct stmmac_priv *priv, return 0; } +/* Max quantum value supported by the MTL DWRR scheduler */ +#define MTL_TXQ_WEIGHT_QUANTUM_MAX 0x1312d0 + +static void stmmac_qdisc_restore_dt_config(struct stmmac_priv *priv) +{ + /* reset to the dt configured algorithm. */ + 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_prog_mtl_tx_algorithms(priv, priv->hw, + priv->plat->tx_sched_algorithm); +} + +static int tc_setup_ets_sched(struct stmmac_priv *priv, + struct tc_ets_qopt_offload *qopt) +{ + struct tc_ets_qopt_offload_replace_params *p = &qopt->replace_params; + struct plat_stmmacenet_data *pdata = priv->plat; + int err, i; + + if (!priv->dma_cap.dcben) + return -EOPNOTSUPP; + + if (qopt->parent != TC_H_ROOT) + return -EOPNOTSUPP; + + if (p->bands > pdata->tx_queues_to_use) + return -EOPNOTSUPP; + + for (i = 0; i < pdata->tx_queues_to_use; i++) { + if (pdata->tx_queues_cfg[i].mode_to_use == MTL_QUEUE_AVB) + return -EOPNOTSUPP; + } + + for (i = 0; i < p->bands; i++) { + if (!p->quanta[i]) + return -EOPNOTSUPP; + + if (p->quanta[i] > MTL_TXQ_WEIGHT_QUANTUM_MAX) + return -EOPNOTSUPP; + } + + err = netif_set_real_num_tx_queues(priv->dev, p->bands); + if (err) + return err; + + for (i = 0; i < pdata->tx_queues_to_use; i++) { + u32 quantum = i < p->bands ? p->quanta[i] : 0; + + priv->qdisc.quanta[i] = quantum; + stmmac_set_mtl_tx_queue_weight(priv, priv->hw, quantum, i); + } + + stmmac_prog_mtl_tx_algorithms(priv, priv->hw, MTL_TX_ALGORITHM_DWRR); + priv->qdisc.algo = MTL_TX_ALGORITHM_DWRR; + priv->qdisc.handle = qopt->handle; + priv->qdisc.bands = p->bands; + priv->qdisc.enable = true; + + return 0; +} + +static int tc_setup_ets_destroy(struct stmmac_priv *priv, + struct tc_ets_qopt_offload *qopt) +{ + if (qopt->parent != TC_H_ROOT || + qopt->handle != priv->qdisc.handle) + return -EOPNOTSUPP; + + if (!priv->qdisc.enable || + priv->qdisc.algo != MTL_TX_ALGORITHM_DWRR) + return -EOPNOTSUPP; + + stmmac_qdisc_restore_dt_config(priv); + + return 0; +} + +static int tc_setup_ets(struct stmmac_priv *priv, + struct tc_ets_qopt_offload *qopt) +{ + switch (qopt->command) { + case TC_ETS_REPLACE: + return tc_setup_ets_sched(priv, qopt); + case TC_ETS_DESTROY: + return tc_setup_ets_destroy(priv, qopt); + case TC_ETS_GRAFT: + break; + case TC_ETS_STATS: + if (qopt->parent == TC_H_ROOT && + qopt->handle == priv->qdisc.handle && + priv->qdisc.enable && + priv->qdisc.algo == MTL_TX_ALGORITHM_DWRR) + break; + fallthrough; + default: + return -EOPNOTSUPP; + } + + return 0; +} + static int tc_query_caps(struct stmmac_priv *priv, struct tc_query_caps_base *base) { @@ -1308,6 +1410,7 @@ const struct stmmac_tc_ops dwmac4_tc_ops = { .setup_cls = tc_setup_cls, .setup_taprio = tc_setup_taprio_without_fpe, .setup_etf = tc_setup_etf, + .setup_ets = tc_setup_ets, .query_caps = tc_query_caps, .setup_mqprio = tc_setup_mqprio_unimplemented, }; @@ -1319,6 +1422,7 @@ const struct stmmac_tc_ops dwmac510_tc_ops = { .setup_cls = tc_setup_cls, .setup_taprio = tc_setup_taprio, .setup_etf = tc_setup_etf, + .setup_ets = tc_setup_ets, .query_caps = tc_query_caps, .setup_mqprio = tc_setup_dwmac510_mqprio, }; -- 2.55.0