RDMA/cxgb4: Fix dereg_skb leak and double free in write_tpt_entry()

When the device is in the fatal error state, write_tpt_entry() returns -EIO
before handing the caller's preallocated skb to the transmit path; its
allocation-failure returns do the same. c4iw_dereg_mr() ignores the error
and frees mhp, leaking mhp->dereg_skb. c4iw_get_dma_mr() instead frees the
skb a second time after dereg_mem() already consumed it, a double free.

Make write_tpt_entry() the sole owner of a non-NULL skb, freeing it on
every return preceding handoff to c4iw_ofld_send(): fatal error, tpt and
stag allocation failure. c4iw_ofld_send() consumes the skb on success and
error alike, so drop the redundant kfree_skb() in c4iw_get_dma_mr() after
dereg_mem().

Fixes: 0f8ab0b6e9 ("RDMA/iw_cxgb4: Low resource fixes for Memory registration")
Link: https://patch.msgid.link/20260726-leaked-mhp-dereg-skb-in-c4iw-dereg-m-v1-1-ebd6df364d53@nvidia.com
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
This commit is contained in:
Leon Romanovsky 2026-07-29 07:21:28 -04:00
parent 229b42d745
commit 373f3716a2

View File

@ -193,7 +193,8 @@ static int _c4iw_write_mem_dma(struct c4iw_rdev *rdev, u32 addr, u32 len,
daddr = dma_map_single(&rdev->lldi.pdev->dev, data, len, DMA_TO_DEVICE);
if (dma_mapping_error(&rdev->lldi.pdev->dev, daddr))
return -1;
return _c4iw_write_mem_inline(rdev, addr, len, data, skb,
wr_waitp);
save = daddr;
while (remain > inline_threshold) {
@ -229,30 +230,12 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
void *data, struct sk_buff *skb,
struct c4iw_wr_wait *wr_waitp)
{
int ret;
if (!rdev->lldi.ulptx_memwrite_dsgl || !use_dsgl) {
ret = _c4iw_write_mem_inline(rdev, addr, len, data, skb,
if (!rdev->lldi.ulptx_memwrite_dsgl || !use_dsgl ||
len <= inline_threshold)
return _c4iw_write_mem_inline(rdev, addr, len, data, skb,
wr_waitp);
goto out;
}
if (len <= inline_threshold) {
ret = _c4iw_write_mem_inline(rdev, addr, len, data, skb,
wr_waitp);
goto out;
}
ret = _c4iw_write_mem_dma(rdev, addr, len, data, skb, wr_waitp);
if (ret) {
pr_warn_ratelimited("%s: dma map failure (non fatal)\n",
pci_name(rdev->lldi.pdev));
ret = _c4iw_write_mem_inline(rdev, addr, len, data, skb,
wr_waitp);
}
out:
return ret;
return _c4iw_write_mem_dma(rdev, addr, len, data, skb, wr_waitp);
}
/*
@ -273,12 +256,16 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry,
u32 stag_idx;
static atomic_t key;
if (c4iw_fatal_error(rdev))
if (c4iw_fatal_error(rdev)) {
kfree_skb(skb);
return -EIO;
}
tpt = kmalloc_obj(*tpt);
if (!tpt)
if (!tpt) {
kfree_skb(skb);
return -ENOMEM;
}
stag_state = stag_state > 0;
stag_idx = (*stag) >> 8;
@ -290,6 +277,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry,
rdev->stats.stag.fail++;
mutex_unlock(&rdev->stats.lock);
kfree(tpt);
kfree_skb(skb);
return -ENOMEM;
}
mutex_lock(&rdev->stats.lock);
@ -463,8 +451,10 @@ struct ib_mr *c4iw_get_dma_mr(struct ib_pd *pd, int acc)
FW_RI_STAG_NSMR, mhp->attr.perms,
mhp->attr.mw_bind_enable, 0, 0, ~0ULL, 0, 0, 0,
NULL, mhp->wr_waitp);
if (ret)
goto err_free_skb;
if (ret) {
kfree_skb(mhp->dereg_skb);
goto err_free_wr_wait;
}
ret = finish_mem_reg(mhp, stag);
if (ret)
@ -473,8 +463,6 @@ struct ib_mr *c4iw_get_dma_mr(struct ib_pd *pd, int acc)
err_dereg_mem:
dereg_mem(&rhp->rdev, mhp->attr.stag, mhp->attr.pbl_size,
mhp->attr.pbl_addr, mhp->dereg_skb, mhp->wr_waitp);
err_free_skb:
kfree_skb(mhp->dereg_skb);
err_free_wr_wait:
c4iw_put_wr_wait(mhp->wr_waitp);
err_free_mhp: