aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorNeil Horman <nhorman@tuxdriver.com>2013-07-16 10:49:41 -0400
committerBen Hutchings <ben@decadent.org.uk>2013-08-02 22:14:50 +0200
commit729c5244dac355ca8c81f402739c218cc3212a10 (patch)
tree60d55c9efde3dbcb76594cb4eb94fce5766b6102 /drivers
parent70513e78db3910edc5ba74ca5f31f6e619acac97 (diff)
atl1e: unmap partially mapped skb on dma error and free skb
[ Upstream commit 584ec4355355ffac43571b02a314d43eb2f7fcbf ] Ben Hutchings pointed out that my recent update to atl1e in commit 352900b583b2852152a1e05ea0e8b579292e731e ("atl1e: fix dma mapping warnings") was missing a bit of code. Specifically it reset the hardware tx ring to its origional state when we hit a dma error, but didn't unmap any exiting mappings from the operation. This patch fixes that up. It also remembers to free the skb in the event that an error occurs, so we don't leak. Untested, as I don't have hardware. I think its pretty straightforward, but please review closely. Signed-off-by: Neil Horman <nhorman@tuxdriver.com> CC: Ben Hutchings <bhutchings@solarflare.com> CC: Jay Cliburn <jcliburn@gmail.com> CC: Chris Snook <chris.snook@gmail.com> CC: "David S. Miller" <davem@davemloft.net> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_main.c24
1 files changed, 23 insertions, 1 deletions
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
index ec08ccd9453..87851f09e92 100644
--- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
@@ -1698,6 +1698,7 @@ static int atl1e_tx_map(struct atl1e_adapter *adapter,
u16 f;
int segment;
int ring_start = adapter->tx_ring.next_to_use;
+ int ring_end;
nr_frags = skb_shinfo(skb)->nr_frags;
segment = (tpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK;
@@ -1741,6 +1742,15 @@ static int atl1e_tx_map(struct atl1e_adapter *adapter,
map_len, PCI_DMA_TODEVICE);
if (dma_mapping_error(&adapter->pdev->dev, tx_buffer->dma)) {
+ /* We need to unwind the mappings we've done */
+ ring_end = adapter->tx_ring.next_to_use;
+ adapter->tx_ring.next_to_use = ring_start;
+ while (adapter->tx_ring.next_to_use != ring_end) {
+ tpd = atl1e_get_tpd(adapter);
+ tx_buffer = atl1e_get_tx_buffer(adapter, tpd);
+ pci_unmap_single(adapter->pdev, tx_buffer->dma,
+ tx_buffer->length, PCI_DMA_TODEVICE);
+ }
/* Reset the tx rings next pointer */
adapter->tx_ring.next_to_use = ring_start;
return -ENOSPC;
@@ -1783,6 +1793,16 @@ static int atl1e_tx_map(struct atl1e_adapter *adapter,
DMA_TO_DEVICE);
if (dma_mapping_error(&adapter->pdev->dev, tx_buffer->dma)) {
+ /* We need to unwind the mappings we've done */
+ ring_end = adapter->tx_ring.next_to_use;
+ adapter->tx_ring.next_to_use = ring_start;
+ while (adapter->tx_ring.next_to_use != ring_end) {
+ tpd = atl1e_get_tpd(adapter);
+ tx_buffer = atl1e_get_tx_buffer(adapter, tpd);
+ dma_unmap_page(&adapter->pdev->dev, tx_buffer->dma,
+ tx_buffer->length, DMA_TO_DEVICE);
+ }
+
/* Reset the ring next to use pointer */
adapter->tx_ring.next_to_use = ring_start;
return -ENOSPC;
@@ -1873,8 +1893,10 @@ static netdev_tx_t atl1e_xmit_frame(struct sk_buff *skb,
return NETDEV_TX_OK;
}
- if (atl1e_tx_map(adapter, skb, tpd))
+ if (atl1e_tx_map(adapter, skb, tpd)) {
+ dev_kfree_skb_any(skb);
goto out;
+ }
atl1e_tx_queue(adapter, tpd_req, tpd);