diff options
| author | Serge Semin <fancer.lancer@gmail.com> | 2025-08-26 19:34:33 +0300 |
|---|---|---|
| committer | Serge Semin <fancer.lancer@gmail.com> | 2026-08-13 20:33:46 +0300 |
| commit | 7f4824d2c5cec98097720165b996936234233a38 (patch) | |
| tree | 84d9ccd9322e115cdab14817a99e906402dc90f6 | |
| parent | 26d5628d45bac9aab4fcd82515f0a4a549b0a4d9 (diff) | |
| download | linux-7f4824d2c5cec98097720165b996936234233a38.tar.gz linux-7f4824d2c5cec98097720165b996936234233a38.zip | |
net: stmmac: Make sure PBLs are valid
In accordance with [1,2,3] the PBL (Programmable Burst Length) values are
only valid if they belong to the set [1, 2, 4, 8, 16, 32]. Specifying any
different value results to Undefined Behaviour. Moreover the maximum value
is also limited. The maximum burst length must not exceed the half of
the Tx/Rx Queue Depth (Queue Depth = Queue or FIFO size / Data width).
Without this requirement being fulfilled the communications will be
fully broken.
Since the system interface data bus width is now available in the driver
private data let's use it to calculate the effective MTL FIFO depth and
make sure that the DMA PBL specified by the platform is correct at least
for a single activated queue in order to avoid hardware UB-related errors.
The best place to do that is to introduce a new stmmac_dma_verify()
method, which would be called on the driver probing stage. It will ensure
that the PBL is valid at least for the default device setup. Besides the
method will contain the rest of the DMA settings verification and
adjustment so to be as coherent as possible.
[1] DesignWare Cores Ethernet MAC Universal Databook, Revision 3.73a,
October 2013, p. 380, Table 6-5.
[2] DesignWare Cores Ethernet Quality-of-Service Databook, Revision 5.20a,
April 2020, p. 1185, Table 17-303 and p. 1190, Table 17-304.
[3] DesignWare Cores XGMAC - 10G Ethernet MAC Databook, Revision 2.11a,
September 2015, p. 471, Table 7-17 and p. 474, Table 7-18.
Fixes: d6ddfacd95c7 ("net: stmmac: Add DMA related callbacks for XGMAC2")
Fixes: f748be531d70 ("stmmac: support new GMAC4")
Fixes: 47dd7a540b8a ("net: add support for STMicroelectronics Ethernet controllers.")
Signed-off-by: Serge Semin <fancer.lancer@gmail.com>
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 10 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 167 |
3 files changed, 151 insertions, 28 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 1e8ae9b17ca3..cd0531b52257 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -266,10 +266,12 @@ struct stmmac_dma_conf { unsigned int dma_buf_sz; /* RX Queue */ + unsigned int rx_queue_max; struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES]; unsigned int dma_rx_size; /* TX Queue */ + unsigned int tx_queue_max; struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES]; unsigned int dma_tx_size; }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 152c2657da6a..058b83584e30 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -1003,18 +1003,14 @@ static void stmmac_get_channels(struct net_device *dev, chan->rx_count = priv->plat->rx_queues_to_use; chan->tx_count = priv->plat->tx_queues_to_use; - chan->max_rx = priv->dma_cap.number_rx_queues; - chan->max_tx = priv->dma_cap.number_tx_queues; + chan->max_rx = priv->dma_conf.rx_queue_max; + chan->max_tx = priv->dma_conf.tx_queue_max; } static int stmmac_set_channels(struct net_device *dev, struct ethtool_channels *chan) { - struct stmmac_priv *priv = netdev_priv(dev); - - if (chan->rx_count > priv->dma_cap.number_rx_queues || - chan->tx_count > priv->dma_cap.number_tx_queues || - !chan->rx_count || !chan->tx_count) + if (!chan->rx_count || !chan->tx_count) return -EINVAL; return stmmac_reinit_queues(dev, chan->rx_count, chan->tx_count); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index f99b189ab571..7fbaa490149b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3391,14 +3391,6 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) u32 chan = 0; int ret = 0; - if (!priv->plat->dma_cfg || !priv->plat->dma_cfg->pbl) { - netdev_err(priv->dev, "Invalid DMA configuration\n"); - return -EINVAL; - } - - if (priv->extend_desc) - priv->plat->dma_cfg->atds = 1; - ret = stmmac_prereset_configure(priv); if (ret) return ret; @@ -4142,18 +4134,11 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) } dma_conf->dma_buf_sz = stmmac_get_bfsize(priv, mtu); - - /* Chose the tx/rx size from the already defined one in the - * priv struct. (if defined) - */ + dma_conf->tx_queue_max = priv->dma_conf.tx_queue_max; dma_conf->dma_tx_size = priv->dma_conf.dma_tx_size; + dma_conf->rx_queue_max = priv->dma_conf.rx_queue_max; dma_conf->dma_rx_size = priv->dma_conf.dma_rx_size; - if (!dma_conf->dma_tx_size) - dma_conf->dma_tx_size = DMA_DEFAULT_TX_SIZE; - if (!dma_conf->dma_rx_size) - dma_conf->dma_rx_size = DMA_DEFAULT_RX_SIZE; - /* Earlier check for TBS */ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) { struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[chan]; @@ -8079,6 +8064,146 @@ static int stmmac_hw_init(struct stmmac_priv *priv) return 0; } +/** + * stmmac_pbl_adjust - DMA PBL adjust and verify. + * @pbl: DMA PBL to verify + * @factor: DMA PBL x8/x4 factor + * @fifodp: MTL FIFO depth + * Description: adjust and verify the passed DMA PBL in accordance with the + * HW requirements. + * Return : 0 on success and errno if invalid PBL specified. + */ +static int stmmac_pbl_adjust(unsigned int *pbl, unsigned int factor, + unsigned int fifodp) +{ + const unsigned int min_pbl = 1, max_pbl = 32; + + /* Make sure PBL is from the set [1, 2, 4, 8, 16, 32] otherwise UB */ + *pbl = rounddown_pow_of_two(*pbl); + if (*pbl < min_pbl || *pbl > max_pbl) + return -EINVAL; + + /* Skip legacy platforms with no Rx/Tx MTL FIFO depth data */ + if (!fifodp) + return 0; + + /* Make sure PBL is within the allowed range - less or equal to half + * the Queue depth (MTL FIFO size in terms of DMA beats). + */ + if (*pbl * factor > fifodp / 2) + return -EINVAL; + + return 0; +} + +/** + * stmmac_dma_verify - verify the DMA settings + * @priv: driver private structure + * Description: make sure the platform-specific DMA-config is valid. If it's + * not then either adjust it in some extent or return an error. + * Return: 0 on success or errno if invalid DMA-config detected. + */ +static int stmmac_dma_verify(struct stmmac_priv *priv) +{ + struct stmmac_dma_cfg *dma_cfg = priv->plat->dma_cfg; + unsigned int factor, rxfifodp, txfifodp; + unsigned int rxmaxq, txmaxq; + int ret; + + /* It's mandatory to have the DMA-config specified */ + if (!dma_cfg) { + dev_err(priv->device, "Missing DMA configuration\n"); + return -EINVAL; + } + + /* Make sure at least 1-beat PBL is setup */ + if (!dma_cfg->pbl) { + dev_warn(priv->device, "No PBL specified, init with minimal 1\n"); + dma_cfg->pbl = 1; + } + + /* Activate extended DMA descriptors if requested */ + if (priv->extend_desc) + priv->plat->dma_cfg->atds = 1; + + /* Only DW QoS Eth v5.20 onwards IPs support DMA-desc prefetch */ + if (priv->synopsys_id < DWMAC_CORE_5_20) + priv->plat->dma_cfg->dche = false; + + /* DMA PBL multiplier had been x4 prior DW GMAC v3.50a */ + if ((priv->plat->core_type == DWMAC_CORE_GMAC && + priv->synopsys_id >= DWMAC_CORE_3_50) || + dwmac_is_xmac(priv->plat->core_type)) { + factor = dma_cfg->pblx8 ? 8 : 1; + } else { + factor = dma_cfg->pblx8 ? 4 : 1; + } + + /* Make sure DMA PBLs are valid and don't exceed HW constraints at + * least for the initially activated queues. + */ + rxfifodp = stmmac_get_rxfifosz(priv) / priv->plat->data_width; + txfifodp = stmmac_get_txfifosz(priv) / priv->plat->data_width; + ret = stmmac_pbl_adjust(&dma_cfg->pbl, factor, min(rxfifodp, txfifodp)); + if (ret) { + dev_err(priv->device, "Invalid DMA PBL specified\n"); + return ret; + } + + if (dma_cfg->txpbl) { + ret = stmmac_pbl_adjust(&dma_cfg->txpbl, factor, txfifodp); + if (ret) { + dev_err(priv->device, "Invalid Tx DMA PBL specified\n"); + return ret; + } + } + + if (dma_cfg->rxpbl) { + ret = stmmac_pbl_adjust(&dma_cfg->rxpbl, factor, rxfifodp); + if (ret) { + dev_err(priv->device, "Invalid Rx DMA PBL specified\n"); + return ret; + } + } + + /* Make sure the amount of the active queues never cause the PBL + * exceeding the half FIFO depth constraint. + */ + priv->dma_conf.rx_queue_max = priv->dma_cap.number_rx_queues; + priv->dma_conf.tx_queue_max = priv->dma_cap.number_tx_queues; + if (dwmac_is_xmac(priv->plat->core_type)) { + rxfifodp = priv->plat->rx_fifo_size / priv->plat->data_width / 2; + rxmaxq = rxfifodp / ((dma_cfg->rxpbl ?: dma_cfg->pbl) * factor); + txfifodp = priv->plat->tx_fifo_size / priv->plat->data_width / 2; + txmaxq = txfifodp / ((dma_cfg->txpbl ?: dma_cfg->pbl) * factor); + } else { + rxmaxq = priv->plat->rx_queues_to_use; + txmaxq = priv->plat->tx_queues_to_use; + } + + if (priv->dma_conf.rx_queue_max > rxmaxq) { + priv->dma_conf.rx_queue_max = rxmaxq; + dev_warn(priv->device, "Reduce Rx queues max to %u due to PBL\n", + rxmaxq); + } else if (!priv->dma_conf.rx_queue_max) { + priv->dma_conf.rx_queue_max = rxmaxq; + } + + if (priv->dma_conf.tx_queue_max > txmaxq) { + priv->dma_conf.tx_queue_max = txmaxq; + dev_warn(priv->device, "Reduce Tx queues max to %u due to PBL\n", + txmaxq); + } else if (!priv->dma_conf.tx_queue_max) { + priv->dma_conf.tx_queue_max = txmaxq; + } + + /* Initialize the default Tx/Rx DMA ring size */ + priv->dma_conf.dma_tx_size = DMA_DEFAULT_TX_SIZE; + priv->dma_conf.dma_rx_size = DMA_DEFAULT_RX_SIZE; + + return 0; +} + static void stmmac_napi_add(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); @@ -8443,10 +8568,10 @@ static int __stmmac_dvr_probe(struct device *device, if (ret) goto error_hw_init; - /* Only DWMAC core version 5.20 onwards supports HW descriptor prefetch. - */ - if (priv->synopsys_id < DWMAC_CORE_5_20) - priv->plat->dma_cfg->dche = false; + /* Verify DMA config */ + ret = stmmac_dma_verify(priv); + if (ret) + goto error_hw_init; stmmac_check_ether_addr(priv); |
