[PATCH net] net: stmmac: fix dma mapping leak in stmmac_tso_xmit()
Lorenzo Bianconi
lorenzo.bianconi at oss.qualcomm.com
Wed Aug 26 06:03:15 PDT 2026
In stmmac_tso_xmit(), if the DMA mapping of an skb fragment fails, the
frame is dropped but the DMA mappings already created for the linear
part and for the fragments mapped before the failure are never
unmapped, leaking DMA mappings.
Fix the leak by walking back over the descriptors used by the frame and
releasing each of them with stmmac_free_tx_buffer(). Moreover, release
the descriptors with stmmac_release_tx_desc() unmapping the DMA buffers.
Fixes: f748be531d70 ("stmmac: support new GMAC4")
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi at oss.qualcomm.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 53 +++++++++++++++--------
1 file changed, 34 insertions(+), 19 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index efa35cfecc4f..64d9356b6226 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -4319,6 +4319,7 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
/**
* stmmac_tso_allocator - close entry point of the driver
* @priv: driver private structure
+ * @entry: TX queue buffer index
* @des: buffer start address
* @total_len: total length to fill in descriptors
* @last_segment: condition for the last descriptor
@@ -4327,8 +4328,9 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
* This function fills descriptor and request new descriptors according to
* buffer length to fill
*/
-static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des,
- int total_len, bool last_segment, u32 queue)
+static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,
+ dma_addr_t des, int total_len,
+ bool last_segment, u32 queue)
{
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
struct dma_desc *desc;
@@ -4340,14 +4342,13 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des,
while (tmp_len > 0) {
dma_addr_t curr_addr;
- tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx,
- priv->dma_conf.dma_tx_size);
- WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
+ *entry = STMMAC_NEXT_ENTRY(*entry, priv->dma_conf.dma_tx_size);
+ WARN_ON(tx_q->tx_skbuff[*entry]);
if (tx_q->tbs & STMMAC_TBS_AVAIL)
- desc = &tx_q->dma_entx[tx_q->cur_tx].basic;
+ desc = &tx_q->dma_entx[*entry].basic;
else
- desc = &tx_q->dma_tx[tx_q->cur_tx];
+ desc = &tx_q->dma_tx[*entry];
curr_addr = des + (total_len - tmp_len);
stmmac_set_desc_addr(priv, desc, curr_addr);
@@ -4500,7 +4501,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dma_desc *desc, *first, *mss_desc = NULL;
struct stmmac_priv *priv = netdev_priv(dev);
- unsigned int first_entry, tx_packets;
+ unsigned int first_entry, entry, tx_packets;
struct stmmac_txq_stats *txq_stats;
struct stmmac_tx_queue *tx_q;
bool set_ic, is_last_segment;
@@ -4563,22 +4564,24 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
}
first_entry = tx_q->cur_tx;
- WARN_ON(tx_q->tx_skbuff[first_entry]);
+ entry = first_entry;
+
+ WARN_ON(tx_q->tx_skbuff[entry]);
if (tx_q->tbs & STMMAC_TBS_AVAIL)
- desc = &tx_q->dma_entx[first_entry].basic;
+ desc = &tx_q->dma_entx[entry].basic;
else
- desc = &tx_q->dma_tx[first_entry];
+ desc = &tx_q->dma_tx[entry];
first = desc;
/* first descriptor: fill Headers on Buf1 */
des = dma_map_single(priv->device, skb->data, skb_headlen(skb),
DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, des))
- goto dma_map_err;
+ goto error;
stmmac_set_desc_addr(priv, first, des);
- stmmac_tso_allocator(priv, des + proto_hdr_len, pay_len,
+ stmmac_tso_allocator(priv, &entry, des + proto_hdr_len, pay_len,
(nfrags == 0), queue);
/* In case two or more DMA transmit descriptors are allocated for this
@@ -4593,8 +4596,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
* this DMA buffer right after the DMA engine completely finishes the
* full buffer transmission.
*/
- stmmac_set_tx_skb_dma_entry(tx_q, tx_q->cur_tx, des, skb_headlen(skb),
- false);
+ stmmac_set_tx_skb_dma_entry(tx_q, entry, des, skb_headlen(skb), false);
/* Prepare fragments */
for (i = 0; i < nfrags; i++) {
@@ -4604,14 +4606,15 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
skb_frag_size(frag),
DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, des))
- goto dma_map_err;
+ goto error_dma_unmap;
- stmmac_tso_allocator(priv, des, skb_frag_size(frag),
+ stmmac_tso_allocator(priv, &entry, des, skb_frag_size(frag),
(i == nfrags - 1), queue);
- stmmac_set_tx_skb_dma_entry(tx_q, tx_q->cur_tx, des,
+ stmmac_set_tx_skb_dma_entry(tx_q, entry, des,
skb_frag_size(frag), true);
}
+ tx_q->cur_tx = entry;
stmmac_set_tx_dma_last_segment(tx_q, tx_q->cur_tx);
@@ -4716,7 +4719,19 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
-dma_map_err:
+error_dma_unmap:
+ for (;;) {
+ desc = stmmac_get_tx_desc(priv, tx_q, first_entry);
+ stmmac_release_tx_desc(priv, desc, priv->descriptor_mode);
+ stmmac_free_tx_buffer(priv, &priv->dma_conf, queue,
+ first_entry);
+ if (first_entry == entry)
+ break;
+
+ first_entry = STMMAC_NEXT_ENTRY(first_entry,
+ priv->dma_conf.dma_tx_size);
+ }
+error:
dev_err(priv->device, "Tx dma map failed\n");
dev_kfree_skb(skb);
priv->xstats.tx_dropped++;
---
base-commit: f967455fb2a5a2079b9eb5823e9ccf359174bf9f
change-id: 20260826-stmmac_dma_unmap_tso-3d8cfa71c6b6
Best regards,
--
Lorenzo Bianconi <lorenzo.bianconi at oss.qualcomm.com>
More information about the linux-arm-kernel
mailing list