diff mbox

[RFC] fix problems with NETIF_F_HIGHDMA in networking drivers v2

Message ID 20100304135738C.fujita.tomonori@lab.ntt.co.jp
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

FUJITA Tomonori March 4, 2010, 4:58 a.m. UTC
On Wed, 03 Mar 2010 20:55:55 -0600
Robert Hancock <hancockrwd@gmail.com> wrote:

> Many networking drivers have issues with the use of the NETIF_F_HIGHDMA flag.
> This flag actually indicates whether or not the device/driver can handle
> skbs located in high memory (as opposed to lowmem). If the flag isn't set and
> the skb is located in highmem, it needs to be copied. 
> There are two problems with this flag:
> 
> -Many drivers only set the flag when they detect they can use 64-bit DMA,
> since otherwise they could receive DMA addresses that they can't handle
> (which on platforms without IOMMU/SWIOTLB support is fatal). This means that if
> 64-bit support isn't available, even buffers located below 4GB will get copied
> unnecessarily.
> 
> -Some drivers set the flag even though they can't actually handle 64-bit DMA,
> which would mean that on platforms without IOMMU/SWIOTLB they would get a DMA
> mapping error if the memory they received happened to be located above 4GB.
> 
> In order to fix this problem, the existing NETIF_F_HIGHDMA flag is split into
> two new flags:
> 
> NETIF_F_DMA_HIGH - indicates if the driver can do DMA to highmem at all
> NETIF_F_DMA_64BIT - indicates the driver can do DMA to 64-bit memory

Why can't you use dev->dma_mask here like the following?

Then you can fix drivers that use the NETIF_F_HIGHDMA flag to indicate
that they don't support 64bit DMA.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Robert Hancock March 26, 2010, 1:03 a.m. UTC | #1
On Wed, Mar 3, 2010 at 10:58 PM, FUJITA Tomonori
<fujita.tomonori@lab.ntt.co.jp> wrote:
> On Wed, 03 Mar 2010 20:55:55 -0600
> Robert Hancock <hancockrwd@gmail.com> wrote:
>
>> Many networking drivers have issues with the use of the NETIF_F_HIGHDMA flag.
>> This flag actually indicates whether or not the device/driver can handle
>> skbs located in high memory (as opposed to lowmem). If the flag isn't set and
>> the skb is located in highmem, it needs to be copied.
>> There are two problems with this flag:
>>
>> -Many drivers only set the flag when they detect they can use 64-bit DMA,
>> since otherwise they could receive DMA addresses that they can't handle
>> (which on platforms without IOMMU/SWIOTLB support is fatal). This means that if
>> 64-bit support isn't available, even buffers located below 4GB will get copied
>> unnecessarily.
>>
>> -Some drivers set the flag even though they can't actually handle 64-bit DMA,
>> which would mean that on platforms without IOMMU/SWIOTLB they would get a DMA
>> mapping error if the memory they received happened to be located above 4GB.
>>
>> In order to fix this problem, the existing NETIF_F_HIGHDMA flag is split into
>> two new flags:
>>
>> NETIF_F_DMA_HIGH - indicates if the driver can do DMA to highmem at all
>> NETIF_F_DMA_64BIT - indicates the driver can do DMA to 64-bit memory
>
> Why can't you use dev->dma_mask here like the following?
>
> Then you can fix drivers that use the NETIF_F_HIGHDMA flag to indicate
> that they don't support 64bit DMA.
>
> diff --git a/net/core/dev.c b/net/core/dev.c
> index bcc490c..b15f94b 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -129,6 +129,7 @@
>  #include <linux/jhash.h>
>  #include <linux/random.h>
>  #include <trace/events/napi.h>
> +#include <linux/pci.h>
>
>  #include "net-sysfs.h"
>
> @@ -1787,14 +1788,21 @@ static inline int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
>  {
>  #ifdef CONFIG_HIGHMEM
>        int i;
> +       if (!(dev->features & NETIF_F_HIGHDMA)) {
> +               for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
> +                       if (PageHighMem(skb_shinfo(skb)->frags[i].page))
> +                               return 1;
> +       }
>
> -       if (dev->features & NETIF_F_HIGHDMA)
> -               return 0;
> -
> -       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
> -               if (PageHighMem(skb_shinfo(skb)->frags[i].page))
> -                       return 1;
> +       if (PCI_DMA_BUS_IS_PHYS) {
> +               struct device *pdev = dev->dev.parent;
>
> +               for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
> +                       dma_addr_t addr = page_to_phys(skb_shinfo(skb)->frags[i].page);
> +                       if (!pdev->dma_mask || addr + PAGE_SIZE - 1 > *pdev->dma_mask)
> +                               return 1;
> +               }
> +       }
>  #endif
>        return 0;
>  }
>

This seems like it could be a reasonable approach. The only thing is
that in this code you're returning 1 if the parent device has no DMA
mask set. Wouldn't it make more sense to return 0 in this case? I'm
assuming that in that situation it's a virtual device not backed by
any hardware and there should be no DMA mask restriction...
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
FUJITA Tomonori March 26, 2010, 3:33 a.m. UTC | #2
On Thu, 25 Mar 2010 19:03:37 -0600
Robert Hancock <hancockrwd@gmail.com> wrote:

> This seems like it could be a reasonable approach. The only thing is
> that in this code you're returning 1 if the parent device has no DMA
> mask set. Wouldn't it make more sense to return 0 in this case? I'm
> assuming that in that situation it's a virtual device not backed by
> any hardware and there should be no DMA mask restriction...

I chose the safer option because I don't know enough how net_device
structure is used. If returning zero in such case is always safe, it's
fine by me. any example of such virtual device driver?
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller March 26, 2010, 3:35 a.m. UTC | #3
From: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Date: Fri, 26 Mar 2010 12:33:12 +0900

> On Thu, 25 Mar 2010 19:03:37 -0600
> Robert Hancock <hancockrwd@gmail.com> wrote:
> 
>> This seems like it could be a reasonable approach. The only thing is
>> that in this code you're returning 1 if the parent device has no DMA
>> mask set. Wouldn't it make more sense to return 0 in this case? I'm
>> assuming that in that situation it's a virtual device not backed by
>> any hardware and there should be no DMA mask restriction...
> 
> I chose the safer option because I don't know enough how net_device
> structure is used. If returning zero in such case is always safe, it's
> fine by me. any example of such virtual device driver?

Like Fujita I'd rather play it safe here.

Even for virtual devices, DMA information up to the root bus
ought to be sane.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/net/core/dev.c b/net/core/dev.c
index bcc490c..b15f94b 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -129,6 +129,7 @@ 
 #include <linux/jhash.h>
 #include <linux/random.h>
 #include <trace/events/napi.h>
+#include <linux/pci.h>
 
 #include "net-sysfs.h"
 
@@ -1787,14 +1788,21 @@  static inline int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
 {
 #ifdef CONFIG_HIGHMEM
 	int i;
+	if (!(dev->features & NETIF_F_HIGHDMA)) {
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+			if (PageHighMem(skb_shinfo(skb)->frags[i].page))
+				return 1;
+	}
 
-	if (dev->features & NETIF_F_HIGHDMA)
-		return 0;
-
-	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
-		if (PageHighMem(skb_shinfo(skb)->frags[i].page))
-			return 1;
+	if (PCI_DMA_BUS_IS_PHYS) {
+		struct device *pdev = dev->dev.parent;
 
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+			dma_addr_t addr = page_to_phys(skb_shinfo(skb)->frags[i].page);
+			if (!pdev->dma_mask || addr + PAGE_SIZE - 1 > *pdev->dma_mask)
+				return 1;
+		}
+	}
 #endif
 	return 0;
 }