Skip to contentSkip to conversation

e1000: Fix DMA mapping error handling on TX

PATCH In review Anton Blanchard 3 participants 8 messages Jan 21, 2010 – Feb 4, 2010

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/2 e1000: Fix DMA mapping error handling on TX

There were a few issues in the DMA mapping error handling in e1000_tx_map which I found via fault injection.

If we fail to map the first descriptor count will end up as -1 but the check of (count >= 0) will still be true since count is unsigned. Instead of changing count to be signed, just simplify the logic.

Secondly, when we wrap the tx ring we rely on i to go negative, but it was unsigned.

---

drivers/net/e1000/e1000_main.c +3 -4
Index: linux.trees.git/drivers/net/e1000/e1000_main.c
===================================================================
@@ -2693,8 +2693,9 @@ static int e1000_tx_map(struct e1000_ada
 	struct pci_dev *pdev = adapter->pdev;
 	struct e1000_buffer *buffer_info;
 	unsigned int len = skb_headlen(skb);
-	unsigned int offset = 0, size, count = 0, i;
+	unsigned int offset = 0, size, count = 0;
 	unsigned int f;
+	int i;
 
 	i = tx_ring->next_to_use;
 
@@ -2802,10 +2803,8 @@ static int e1000_tx_map(struct e1000_ada
 dma_error:
 	dev_err(&pdev->dev, "TX DMA map failed\n");
 	buffer_info->dma = 0;
-	count--;
 
-	while (count >= 0) {
-		count--;
+	while (count--) {
 		i--;
 		if (i < 0)
 			i += tx_ring->count;
#1 lore raw

PATCH 2/2 e1000: Fix DMA mapping error handling on RX

Check for error return from pci_map_single/pci_map_page and clean up.

With this and the previous patch the driver was able to handle a significant percentage of errors (I set the fault injection rate to 10% and could still download large files at a reasonable speed).

---

I wasn't able to stress the jumbo frame path, so that part could do with some more eyes.

drivers/net/e1000/e1000_main.c +18 -1
Index: linux.trees.git/drivers/net/e1000/e1000_main.c
===================================================================
@@ -4014,11 +4014,21 @@ check_page:
 			}
 		}
 
-		if (!buffer_info->dma)
+		if (!buffer_info->dma) {
 			buffer_info->dma = pci_map_page(pdev,
 			                                buffer_info->page, 0,
 			                                buffer_info->length,
 			                                PCI_DMA_FROMDEVICE);
+			if (pci_dma_mapping_error(pdev, buffer_info->dma)) {
+				put_page(buffer_info->page);
+				dev_kfree_skb(skb);
+				buffer_info->page = NULL;
+				buffer_info->skb = NULL;
+				buffer_info->dma = 0;
+				adapter->alloc_rx_buff_failed++;
+				break; /* while !buffer_info->skb */
+			}
+		}
 
 		rx_desc = E1000_RX_DESC(*rx_ring, i);
 		rx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
@@ -4109,6 +4119,13 @@ map_skb:
 						  skb->data,
 						  buffer_info->length,
 						  PCI_DMA_FROMDEVICE);
+		if (pci_dma_mapping_error(pdev, buffer_info->dma)) {
+			dev_kfree_skb(skb);
+			buffer_info->skb = NULL;
+			buffer_info->dma = 0;
+			adapter->alloc_rx_buff_failed++;
+			break; /* while !buffer_info->skb */
+		}
 
 		/*
 		 * XXX if it was allocated cleanly it will never map to a
#2 lore raw

Re: [PATCH 2/2] e1000: Fix DMA mapping error handling on RX

Quoted 44 lines · Anton Blanchard

Check for error return from pci_map_single/pci_map_page and clean up.

With this and the previous patch the driver was able to handle a significant percentage of errors (I set the fault injection rate to 10% and could still download large files at a reasonable speed).

---

I wasn't able to stress the jumbo frame path, so that part could do with some more eyes.

Index: linux.trees.git/drivers/net/e1000/e1000_main.c
===================================================================

drivers/net/e1000/e1000_main.c      2010-01-21 11:15:54.000000000 +1100 renamed from drivers/net/e1000/e1000_main.c 2010-01-21 11:13:10.000000000 +1100 +11 -1
@@ -4014,11 +4014,21 @@ check_page:
                        }
                }
 
-               if (!buffer_info->dma)
+               if (!buffer_info->dma) {
                        buffer_info->dma = pci_map_page(pdev,
                                                        buffer_info->page, 0,
                                                        buffer_info->length,
                                                        PCI_DMA_FROMDEVICE);
+                       if (pci_dma_mapping_error(pdev, buffer_info->dma)) {
+                               put_page(buffer_info->page);
+                               dev_kfree_skb(skb);
+                               buffer_info->page = NULL;
+                               buffer_info->skb = NULL;
+                               buffer_info->dma = 0;
+                               adapter->alloc_rx_buff_failed++;
+                               break; /* while !buffer_info->skb */
+                       }
+               }
 
                rx_desc = E1000_RX_DESC(*rx_ring, i);
                rx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
@@ -4109,6 +4119,13 @@ map_skb:
                                                  skb->data,
                                                  buffer_info->length,
                                                  PCI_DMA_FROMDEVICE);

drivers/net/e1000/e1000_main.c      2010-01-21 11:15:54.000000000 +1100 map_skb: · line 4131

Quoted 10 lines
drivers/net/e1000/e1000_main.c      2010-01-21 11:15:54.000000000 +1100 renamed from drivers/net/e1000/e1000_main.c 2010-01-21 11:13:10.000000000 +1100 +7 -0
+               if (pci_dma_mapping_error(pdev, buffer_info->dma)) {
+                       dev_kfree_skb(skb);
+                       buffer_info->skb = NULL;
+                       buffer_info->dma = 0;
+                       adapter->alloc_rx_buff_failed++;
+                       break; /* while !buffer_info->skb */
+               }
 
                /*
                 * XXX if it was allocated cleanly it will never map to a

I have added this patch to my queue of patches for review and testing. Thanks.

Signature · 2 lines
Cheers,
Jeff
#3 lore raw
In reply to Anton Blanchard
Quoted 35 lines · Anton Blanchard

There were a few issues in the DMA mapping error handling in e1000_tx_map which I found via fault injection.

If we fail to map the first descriptor count will end up as -1 but the check of (count >= 0) will still be true since count is unsigned. Instead of changing count to be signed, just simplify the logic.

Secondly, when we wrap the tx ring we rely on i to go negative, but it was unsigned.

---

Index: linux.trees.git/drivers/net/e1000/e1000_main.c
===================================================================

drivers/net/e1000/e1000_main.c      2010-01-21 11:12:52.000000000 +1100 renamed from drivers/net/e1000/e1000_main.c 2010-01-21 11:10:00.000000000 +1100 +2 -1
@@ -2693,8 +2693,9 @@ static int e1000_tx_map(struct e1000_ada
        struct pci_dev *pdev = adapter->pdev;
        struct e1000_buffer *buffer_info;
        unsigned int len = skb_headlen(skb);
-       unsigned int offset = 0, size, count = 0, i;
+       unsigned int offset = 0, size, count = 0;
        unsigned int f;
+       int i;
 
        i = tx_ring->next_to_use;
 
@@ -2802,10 +2803,8 @@ static int e1000_tx_map(struct e1000_ada

 dma_error:
       dev_err(&pdev->dev, "TX DMA map failed\n"); buffer_info->dma = 0;
-       count--;

Quoted 6 lines

-       while (count >= 0) {
-               count--;
+       while (count--) {
               i--;
               if (i < 0)
                       i += tx_ring->count;

This patch does not apply to the current e1000 driver in net-2.6, much of this patch has already been corrected (applied) by Roel Kluin recent patch.

Signature · 2 lines
Cheers,
Jeff
#4 lore raw

Hi,

Quoted 3 lines

This patch does not apply to the current e1000 driver in net-2.6, much of this patch has already been corrected (applied) by Roel Kluin recent patch.

Sorry I was basing off net-next. I just compared it to my fix and looks like the patch in net-2.6 has an off by one error doesn't it?

Anton

--

Subject: [PATCH] e1000: Fix DMA mapping error handling on TX

e1000_tx_map has an off by one error in the dma mapping error cleanup path. We decrement count and never clean the first successfully mapped descriptor.

---

drivers/net/e1000/e1000_main.c +0 -2
index d29bb53..427cedd 100644
@@ -2790,8 +2790,6 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
 dma_error:
 	dev_err(&pdev->dev, "TX DMA map failed\n");
 	buffer_info->dma = 0;
-	if (count)
-		count--;
 
 	while (count--) {
 		if (i==0)

------------------------------------------------------------------------------
Throughout its 18-year history, RSA Conference consistently attracts the world's best and brightest in the field, creating opportunities for Conference attendees to learn about information security's most important issues through interactions with peers, luminaries and emerging and established companies.
http://p.sf.net/sfu/rsaconf-dev2dev
_______________________________________________
E1000-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/e1000-devel
To learn more about Intel&#174; Ethernet, visit http://communities.intel.com/community/wired

#5 lore raw
Quoted 7 lines · Anton Blanchard

Hi,

Quoted 3 lines

This patch does not apply to the current e1000 driver in net-2.6, much of this patch has already been corrected (applied) by Roel Kluin recent patch.

Quoted 7 lines

Sorry I was basing off net-next. I just compared it to my fix and looks like the patch in net-2.6 has an off by one error doesn't it?

Anton

--

This was discussed during our code review of Roel's patch, and it was found that there was not an issue. But I will review the code again to ensure that there is not "an off by one error". Thanks for looking at this.

Signature · 14 lines
Cheers,
Jeff

------------------------------------------------------------------------------
Throughout its 18-year history, RSA Conference consistently attracts the
world's best and brightest in the field, creating opportunities for Conference
attendees to learn about information security's most important issues through
interactions with peers, luminaries and emerging and established companies.
http://p.sf.net/sfu/rsaconf-dev2dev
_______________________________________________
E1000-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/e1000-devel
To learn more about Intel&#174; Ethernet, visit http://communities.intel.com/community/wired
#6 lore raw
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

This patch does not apply to the current e1000 driver in net-2.6, much of this patch has already been corrected (applied) by Roel Kluin recent patch.

Quoted 7 lines
Quoted 2 lines

Sorry I was basing off net-next. I just compared it to my fix and looks like the patch in net-2.6 has an off by one error doesn't it?

This was discussed during our code review of Roel's patch, and it was found that there was not an issue. But I will review the code again to ensure that there is not "an off by one error". Thanks for looking at this.

He is right, as also reported by Juha Leppanen:

Quoted 10 lines

Before your patch I suppose the logic disregarding the signed/unsigned error was :
1) if count==0, no unmapping/freeing inside while loop
2) if count>0, do 'count' loops unmapping/freeing

After your patch the logic is :
1) if count==0, no unmapping/freeing inside while loop
1) if count==1, no unmapping/freeing inside while loop
2) if count>1, do 'count-1' loops unmapping/freeing

Can tx_ring->count be zero? I hope not.

His suggested fix works:

Quoted 10 lines

dma_error:

	dev_err(&pdev->dev, "TX DMA map failed\n");
	buffer_info->dma = 0;
drivers/net/e1000/e1000_main.c +0 -2
-	if (count)
-		count--;
	while (count--) {
		if (i==0)
(quoted diff) +1 -1
-			i += tx_ring->count;
+			i = tx_ring->count;
Quoted 7 lines
		i--;
		buffer_info = &tx_ring->buffer_info[i];
		e1000_unmap_and_free_tx_resource(adapter, buffer_info);
	}

	return 0;
}

This affects the patches:
[PATCH] e1000: Fix tests of unsigned in e1000_tx_map() and the other patch in the same thread.

Do you want me to send a delta patch?

Roel

------------------------------------------------------------------------------
The Planet: dedicated and managed hosting, cloud storage, colocation
Stay online with enterprise data centers and the best network in the business Choose flexible plans and management services without long-term contracts Personal 24x7 support from experience hosting pros just a phone call away.
http://p.sf.net/sfu/theplanet-com
_______________________________________________
E1000-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/e1000-devel
To learn more about Intel&#174; Ethernet, visit http://communities.intel.com/community/wired

#7 lore raw

These functions have off by one errors in their dma mapping error cleanup paths. We decrement count and never clean the first successfully mapped descriptor.

---
 drivers/net/e1000e/netdev.c    |    4 +---
 drivers/net/igbvf/netdev.c     |    4 +---
 drivers/net/ixgb/ixgb_main.c   |    4 +---
 drivers/net/ixgbe/ixgbe_main.c |    4 +---
 4 files changed, 4 insertions(+), 12 deletions(-)

My previous fix inadvertently introduced this change.

The e1000_tx_map() function in drivers/net/e1000/e1000_main.c is already fixed by the patch sent by Anton Blanchard that can be found here:

http://www.mail-archive.com/[email protected]/msg02313.html

drivers/net/e1000e/netdev.c +1 -3
index 57f149b..57c3d44 100644
@@ -3967,12 +3967,10 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
 dma_error:
 	dev_err(&pdev->dev, "TX DMA map failed\n");
 	buffer_info->dma = 0;
-	if (count)
-		count--;
 
 	while (count--) {
 		if (i==0)
-			i += tx_ring->count;
+			i = tx_ring->count;
 		i--;
 		buffer_info = &tx_ring->buffer_info[i];
 		e1000_put_txbuf(adapter, buffer_info);;
drivers/net/igbvf/netdev.c +1 -3
index 2aa71a7..3b12603 100644
@@ -2164,13 +2164,11 @@ dma_error:
 	buffer_info->length = 0;
 	buffer_info->next_to_watch = 0;
 	buffer_info->mapped_as_page = false;
-	if (count)
-		count--;
 
 	/* clear timestamp and dma mappings for remaining portion of packet */
 	while (count--) {
 		if (i==0)
-			i += tx_ring->count;
+			i = tx_ring->count;
 		i--;
 		buffer_info = &tx_ring->buffer_info[i];
 		igbvf_put_txbuf(adapter, buffer_info);
drivers/net/ixgb/ixgb_main.c +1 -3
index 593d1a4..de9b36c 100644
@@ -1363,12 +1363,10 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb,
 dma_error:
 	dev_err(&pdev->dev, "TX DMA map failed\n");
 	buffer_info->dma = 0;
-	if (count)
-		count--;
 
 	while (count--) {
 		if (i==0)
-			i += tx_ring->count;
+			i = tx_ring->count;
 		i--;
 		buffer_info = &tx_ring->buffer_info[i];
 		ixgb_unmap_and_free_tx_resource(adapter, buffer_info);
drivers/net/ixgbe/ixgbe_main.c +1 -3
index b5f64ad..1713258 100644
@@ -5167,13 +5167,11 @@ dma_error:
 	tx_buffer_info->dma = 0;
 	tx_buffer_info->time_stamp = 0;
 	tx_buffer_info->next_to_watch = 0;
-	if (count)
-		count--;
 
 	/* clear timestamp and dma mappings for remaining portion of packet */
 	while (count--) {
 		if (i==0)
-			i += tx_ring->count;
+			i = tx_ring->count;
 		i--;
 		tx_buffer_info = &tx_ring->tx_buffer_info[i];
 		ixgbe_unmap_and_free_tx_resource(adapter, tx_buffer_info);