net: qlogic: Fix error paths in ql_alloc_large_buffers()
ql_alloc_large_buffers() has the usual RX buffer allocation loop where it allocates skbs and maps them for DMA. It also treats failure as a fatal error. There are (at least) three bugs in the error paths: 1. ql_free_large_buffers() assumes that the lrg_buf[] entry for the first buffer that couldn't be allocated will have .skb == NULL. But the qla_buf[] array is not zero-initialised. 2. ql_free_large_buffers() DMA-unmaps all skbs in lrg_buf[]. This is incorrect for the last allocated skb, if DMA mapping failed. 3. Commit1acb8f2a7a
("net: qlogic: Fix memory leak in ql_alloc_large_buffers") added a direct call to dev_kfree_skb_any() after the skb is recorded in lrg_buf[], so ql_free_large_buffers() will double-free it. The bugs are somewhat inter-twined, so fix them all at once: * Clear each entry in qla_buf[] before attempting to allocate an skb for it. This goes half-way to fixing bug 1. * Set the .skb field only after the skb is DMA-mapped. This fixes the rest. Fixes:1357bfcf71
("qla3xxx: Dynamically size the rx buffer queue ...") Fixes:0f8ab89e82
("qla3xxx: Check return code from pci_map_single() ...") Fixes:1acb8f2a7a
("net: qlogic: Fix memory leak in ql_alloc_large_buffers") Signed-off-by: Ben Hutchings <ben@decadent.org.uk> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
951c6db954
commit
cad46039e4
@ -2756,6 +2756,9 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev)
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
for (i = 0; i < qdev->num_large_buffers; i++) {
|
for (i = 0; i < qdev->num_large_buffers; i++) {
|
||||||
|
lrg_buf_cb = &qdev->lrg_buf[i];
|
||||||
|
memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb));
|
||||||
|
|
||||||
skb = netdev_alloc_skb(qdev->ndev,
|
skb = netdev_alloc_skb(qdev->ndev,
|
||||||
qdev->lrg_buffer_len);
|
qdev->lrg_buffer_len);
|
||||||
if (unlikely(!skb)) {
|
if (unlikely(!skb)) {
|
||||||
@ -2766,11 +2769,7 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev)
|
|||||||
ql_free_large_buffers(qdev);
|
ql_free_large_buffers(qdev);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
lrg_buf_cb = &qdev->lrg_buf[i];
|
|
||||||
memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb));
|
|
||||||
lrg_buf_cb->index = i;
|
lrg_buf_cb->index = i;
|
||||||
lrg_buf_cb->skb = skb;
|
|
||||||
/*
|
/*
|
||||||
* We save some space to copy the ethhdr from first
|
* We save some space to copy the ethhdr from first
|
||||||
* buffer
|
* buffer
|
||||||
@ -2792,6 +2791,7 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lrg_buf_cb->skb = skb;
|
||||||
dma_unmap_addr_set(lrg_buf_cb, mapaddr, map);
|
dma_unmap_addr_set(lrg_buf_cb, mapaddr, map);
|
||||||
dma_unmap_len_set(lrg_buf_cb, maplen,
|
dma_unmap_len_set(lrg_buf_cb, maplen,
|
||||||
qdev->lrg_buffer_len -
|
qdev->lrg_buffer_len -
|
||||||
|
Reference in New Issue
Block a user