Re: [PATCH RFC] fix problems with NETIF_F_HIGHDMA in networking drivers v2
From: Robert Hancock <hidden>
Date: 2010-03-26 01:03:41
Also in:
lkml
On Wed, Mar 3, 2010 at 10:58 PM, FUJITA Tomonori [off-list ref] wrote:
quoted hunk ↗ jump to hunk
On Wed, 03 Mar 2010 20:55:55 -0600 Robert Hancock [off-list ref] wrote:quoted
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 memoryWhy 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...