public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCH] x86 calgary: add fallback dma_ops]]
@ 2008-05-08 21:40 Alexis Bruemmer
  2008-05-08 23:13 ` FUJITA Tomonori
  0 siblings, 1 reply; 5+ messages in thread
From: Alexis Bruemmer @ 2008-05-08 21:40 UTC (permalink / raw)
  To: linux-kernel; +Cc: Muli

Currently the calgary code can give drivers addresses above 4GB which is
very bad for hardware that is only 32bit DMA addressable.  This patch
"teaches" calgary to fallback to the appropriate dma_ops when it
encounters a device/bus which is not behind the Calgary/CalIOC2.  I
believe there is a better way to do this and am open for ideas, but for
now this certainly fixes the badness.


Signed-off-by Alexis Bruemmer <alexisb@us.ibm.com>

---

Index: linux-2.6.26-rc1/arch/x86/kernel/pci-nommu.c
===================================================================
--- linux-2.6.26-rc1.orig/arch/x86/kernel/pci-nommu.c
+++ linux-2.6.26-rc1/arch/x86/kernel/pci-nommu.c
@@ -10,6 +10,7 @@
 #include <asm/gart.h>
 #include <asm/processor.h>
 #include <asm/dma.h>
+#include <asm/calgary.h>

 static int
 check_addr(char *name, struct device *hwdev, dma_addr_t bus, size_t size)
@@ -92,6 +93,10 @@ const struct dma_mapping_ops nommu_dma_o

 void __init no_iommu_init(void)
 {
+#ifdef CONFIG_CALGARY_IOMMU
+	if (use_calgary && (end_pfn < MAX_DMA32_PFN))
+		fallback_dma_ops = &nommu_dma_ops;
+#endif
 	if (dma_ops)
 		return;

Index: linux-2.6.26-rc1/arch/x86/kernel/pci-swiotlb_64.c
===================================================================
--- linux-2.6.26-rc1.orig/arch/x86/kernel/pci-swiotlb_64.c
+++ linux-2.6.26-rc1/arch/x86/kernel/pci-swiotlb_64.c
@@ -8,6 +8,7 @@
 #include <asm/gart.h>
 #include <asm/swiotlb.h>
 #include <asm/dma.h>
+#include <asm/calgary.h>

 int swiotlb __read_mostly;

@@ -47,4 +48,10 @@ void __init pci_swiotlb_init(void)
 		swiotlb_init();
 		dma_ops = &swiotlb_dma_ops;
 	}
+#ifdef CONFIG_CALGARY_IOMMU
+	else if (use_calgary && (end_pfn > MAX_DMA32_PFN)) {
+		swiotlb_init();
+		fallback_dma_ops = &swiotlb_dma_ops;
+	}
+#endif
 }
Index: linux-2.6.26-rc1/include/asm-x86/calgary.h
===================================================================
--- linux-2.6.26-rc1.orig/include/asm-x86/calgary.h
+++ linux-2.6.26-rc1/include/asm-x86/calgary.h
@@ -60,6 +60,7 @@ struct cal_chipset_ops {
 #define TCE_TABLE_SIZE_8M		7

 extern int use_calgary;
+extern const struct dma_mapping_ops* fallback_dma_ops;

 #ifdef CONFIG_CALGARY_IOMMU
 extern int calgary_iommu_init(void);
Index: linux-2.6.26-rc1/arch/x86/kernel/pci-calgary_64.c
===================================================================
--- linux-2.6.26-rc1.orig/arch/x86/kernel/pci-calgary_64.c
+++ linux-2.6.26-rc1/arch/x86/kernel/pci-calgary_64.c
@@ -50,6 +50,7 @@ int use_calgary __read_mostly = 1;
 #else
 int use_calgary __read_mostly = 0;
 #endif /* CONFIG_CALGARY_DEFAULT_ENABLED */
+const struct dma_mapping_ops* fallback_dma_ops;

 #define PCI_DEVICE_ID_IBM_CALGARY 0x02a1
 #define PCI_DEVICE_ID_IBM_CALIOC2 0x0308
@@ -410,22 +411,6 @@ static void calgary_unmap_sg(struct devi
 	}
 }

-static int calgary_nontranslate_map_sg(struct device* dev,
-	struct scatterlist *sg, int nelems, int direction)
-{
-	struct scatterlist *s;
-	int i;
-
-	for_each_sg(sg, s, nelems, i) {
-		struct page *p = sg_page(s);
-
-		BUG_ON(!p);
-		s->dma_address = virt_to_bus(sg_virt(s));
-		s->dma_length = s->length;
-	}
-	return nelems;
-}
-
 static int calgary_map_sg(struct device *dev, struct scatterlist *sg,
 	int nelems, int direction)
 {
@@ -437,7 +422,7 @@ static int calgary_map_sg(struct device 
 	int i;

 	if (!translation_enabled(tbl))
-		return calgary_nontranslate_map_sg(dev, sg, nelems, direction);
+		return fallback_dma_ops->map_sg(dev, sg, nelems, direction);

 	for_each_sg(sg, s, nelems, i) {
 		BUG_ON(!sg_page(s));
@@ -480,13 +465,13 @@ static dma_addr_t calgary_map_single(str
 	unsigned int npages;
 	struct iommu_table *tbl = find_iommu_table(dev);

+	if (!translation_enabled(tbl))
+		return fallback_dma_ops->map_single(dev, paddr, size, direction);
+
 	uaddr = (unsigned long)vaddr;
 	npages = num_dma_pages(uaddr, size);

-	if (translation_enabled(tbl))
-		dma_handle = iommu_alloc(dev, tbl, vaddr, npages, direction);
-	else
-		dma_handle = virt_to_bus(vaddr);
+	dma_handle = iommu_alloc(dev, tbl, vaddr, npages, direction);

 	return dma_handle;
 }
@@ -497,8 +482,10 @@ static void calgary_unmap_single(struct 
 	struct iommu_table *tbl = find_iommu_table(dev);
 	unsigned int npages;

-	if (!translation_enabled(tbl))
+	if (!translation_enabled(tbl)) {
+		fallback_dma_ops->unmap_single(dev, dma_handle, size, direction);
 		return;
+	}

 	npages = num_dma_pages(dma_handle, size);
 	iommu_free(tbl, dma_handle, npages);
@@ -512,6 +499,9 @@ static void* calgary_alloc_coherent(stru
 	unsigned int npages, order;
 	struct iommu_table *tbl = find_iommu_table(dev);

+	if (!translation_enabled(tbl))
+		return fallback_dma_ops->alloc_coherent(dev, size, dma_handle, flag);
+
 	size = PAGE_ALIGN(size); /* size rounded up to full pages */
 	npages = size >> PAGE_SHIFT;
 	order = get_order(size);
@@ -522,15 +512,12 @@ static void* calgary_alloc_coherent(stru
 		goto error;
 	memset(ret, 0, size);

-	if (translation_enabled(tbl)) {
-		/* set up tces to cover the allocated range */
-		mapping = iommu_alloc(dev, tbl, ret, npages, DMA_BIDIRECTIONAL);
-		if (mapping == bad_dma_address)
-			goto free;
-
-		*dma_handle = mapping;
-	} else /* non translated slot */
-		*dma_handle = virt_to_bus(ret);
+	/* set up tces to cover the allocated range */
+	mapping = iommu_alloc(dev, tbl, ret, npages, DMA_BIDIRECTIONAL);
+	if (mapping == bad_dma_address)
+		goto free;
+
+	*dma_handle = mapping;

 	return ret;



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC][PATCH] x86 calgary: add fallback dma_ops]]
  2008-05-08 21:40 [RFC][PATCH] x86 calgary: add fallback dma_ops]] Alexis Bruemmer
@ 2008-05-08 23:13 ` FUJITA Tomonori
  2008-05-08 23:41   ` Alexis Bruemmer
  0 siblings, 1 reply; 5+ messages in thread
From: FUJITA Tomonori @ 2008-05-08 23:13 UTC (permalink / raw)
  To: alexisb; +Cc: linux-kernel, muli

On Thu, 08 May 2008 14:40:20 -0700
Alexis Bruemmer <alexisb@us.ibm.com> wrote:

> Currently the calgary code can give drivers addresses above 4GB which is
> very bad for hardware that is only 32bit DMA addressable.  This patch
> "teaches" calgary to fallback to the appropriate dma_ops when it
> encounters a device/bus which is not behind the Calgary/CalIOC2.  I
> believe there is a better way to do this and am open for ideas, but for
> now this certainly fixes the badness.

I'm not sure that I correctly understand what you want. You mean that
the Calgary IOMMU code ignores device's dma_mask and gives addresses
above 4GB or the Calgary IOMMU code wrongly handles devices that are
not behind the Calgary?

If you refers to the former problem, other IOMMU implementations do
something like this.


diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c
index e28ec49..2a977bc 100644
--- a/arch/x86/kernel/pci-calgary_64.c
+++ b/arch/x86/kernel/pci-calgary_64.c
@@ -268,21 +268,37 @@ static unsigned long iommu_range_alloc(struct device *dev,
 {
 	unsigned long flags;
 	unsigned long offset;
+	unsigned long limit;
 	unsigned long boundary_size;
+	unsigned long start;
+	unsigned long mask;
 
 	boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
 			      PAGE_SIZE) >> PAGE_SHIFT;
 
 	BUG_ON(npages == 0);
 
+	if (dev->dma_mask)
+		mask = *dev->dma_mask >> PAGE_SHIFT;
+	else
+		mask = 0xfffffffful >> PAGE_SHIFT;
+
+	limit = tbl->it_size;
+	start = tbl->it_hint;
+
+	if (limit > mask) {
+		limit = mask + 1;
+		start = tbl->it_hint & mask;
+	}
+
 	spin_lock_irqsave(&tbl->it_lock, flags);
 
-	offset = iommu_area_alloc(tbl->it_map, tbl->it_size, tbl->it_hint,
+	offset = iommu_area_alloc(tbl->it_map, limit, start,
 				  npages, 0, boundary_size, 0);
 	if (offset == ~0UL) {
 		tbl->chip_ops->tce_cache_blast(tbl);
 
-		offset = iommu_area_alloc(tbl->it_map, tbl->it_size, 0,
+		offset = iommu_area_alloc(tbl->it_map, limit, 0,
 					  npages, 0, boundary_size, 0);
 		if (offset == ~0UL) {
 			printk(KERN_WARNING "Calgary: IOMMU full.\n");

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [RFC][PATCH] x86 calgary: add fallback dma_ops]]
  2008-05-08 23:13 ` FUJITA Tomonori
@ 2008-05-08 23:41   ` Alexis Bruemmer
  2008-05-09  0:23     ` FUJITA Tomonori
  0 siblings, 1 reply; 5+ messages in thread
From: Alexis Bruemmer @ 2008-05-08 23:41 UTC (permalink / raw)
  To: FUJITA Tomonori; +Cc: linux-kernel, muli

On Fri, 2008-05-09 at 08:13 +0900, FUJITA Tomonori wrote:
> On Thu, 08 May 2008 14:40:20 -0700
> Alexis Bruemmer <alexisb@us.ibm.com> wrote:
> 
> > Currently the calgary code can give drivers addresses above 4GB which is
> > very bad for hardware that is only 32bit DMA addressable.  This patch
> > "teaches" calgary to fallback to the appropriate dma_ops when it
> > encounters a device/bus which is not behind the Calgary/CalIOC2.  I
> > believe there is a better way to do this and am open for ideas, but for
> > now this certainly fixes the badness.
> 
> I'm not sure that I correctly understand what you want. You mean that
> the Calgary IOMMU code ignores device's dma_mask and gives addresses
> above 4GB or the Calgary IOMMU code wrongly handles devices that are
> not behind the Calgary?
The real issue is the latter-- the Calgary IOMMU code does not properly
handle devices that are not behind the Calgary/CalIO2.
> 
> If you refers to the former problem, other IOMMU implementations do
> something like this.
> 
> 
> diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c
> index e28ec49..2a977bc 100644
> --- a/arch/x86/kernel/pci-calgary_64.c
> +++ b/arch/x86/kernel/pci-calgary_64.c
> @@ -268,21 +268,37 @@ static unsigned long iommu_range_alloc(struct device *dev,
>  {
>  	unsigned long flags;
>  	unsigned long offset;
> +	unsigned long limit;
>  	unsigned long boundary_size;
> +	unsigned long start;
> +	unsigned long mask;
> 
>  	boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
>  			      PAGE_SIZE) >> PAGE_SHIFT;
> 
>  	BUG_ON(npages == 0);
> 
> +	if (dev->dma_mask)
> +		mask = *dev->dma_mask >> PAGE_SHIFT;
> +	else
> +		mask = 0xfffffffful >> PAGE_SHIFT;
> +
> +	limit = tbl->it_size;
> +	start = tbl->it_hint;
> +
> +	if (limit > mask) {
> +		limit = mask + 1;
> +		start = tbl->it_hint & mask;
> +	}
> +
>  	spin_lock_irqsave(&tbl->it_lock, flags);
> 
> -	offset = iommu_area_alloc(tbl->it_map, tbl->it_size, tbl->it_hint,
> +	offset = iommu_area_alloc(tbl->it_map, limit, start,
>  				  npages, 0, boundary_size, 0);
>  	if (offset == ~0UL) {
>  		tbl->chip_ops->tce_cache_blast(tbl);
> 
> -		offset = iommu_area_alloc(tbl->it_map, tbl->it_size, 0,
> +		offset = iommu_area_alloc(tbl->it_map, limit, 0,
>  					  npages, 0, boundary_size, 0);
>  		if (offset == ~0UL) {
>  			printk(KERN_WARNING "Calgary: IOMMU full.\n");


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC][PATCH] x86 calgary: add fallback dma_ops]]
  2008-05-08 23:41   ` Alexis Bruemmer
@ 2008-05-09  0:23     ` FUJITA Tomonori
  2008-05-11 10:08       ` Muli Ben-Yehuda
  0 siblings, 1 reply; 5+ messages in thread
From: FUJITA Tomonori @ 2008-05-09  0:23 UTC (permalink / raw)
  To: alexisb; +Cc: fujita.tomonori, linux-kernel, muli

On Thu, 08 May 2008 16:41:51 -0700
Alexis Bruemmer <alexisb@us.ibm.com> wrote:

> On Fri, 2008-05-09 at 08:13 +0900, FUJITA Tomonori wrote:
> > On Thu, 08 May 2008 14:40:20 -0700
> > Alexis Bruemmer <alexisb@us.ibm.com> wrote:
> > 
> > > Currently the calgary code can give drivers addresses above 4GB which is
> > > very bad for hardware that is only 32bit DMA addressable.  This patch
> > > "teaches" calgary to fallback to the appropriate dma_ops when it
> > > encounters a device/bus which is not behind the Calgary/CalIOC2.  I
> > > believe there is a better way to do this and am open for ideas, but for
> > > now this certainly fixes the badness.
> > 
> > I'm not sure that I correctly understand what you want. You mean that
> > the Calgary IOMMU code ignores device's dma_mask and gives addresses
> > above 4GB or the Calgary IOMMU code wrongly handles devices that are
> > not behind the Calgary?
> The real issue is the latter-- the Calgary IOMMU code does not properly
> handle devices that are not behind the Calgary/CalIO2.

Thanks, now I see why you use swiotlb for such devices in the case of
end_pfn > MAX_DMA32_PFN and no_dma_ops works for them in the case of
of end_pfn < MAX_DMA32_PFN.

Can we put a pointer to dma_ops in struct device (archdata) like POWER
does? The way to setup and handle x86 IOMMUs seems to become hacky day
by day.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC][PATCH] x86 calgary: add fallback dma_ops]]
  2008-05-09  0:23     ` FUJITA Tomonori
@ 2008-05-11 10:08       ` Muli Ben-Yehuda
  0 siblings, 0 replies; 5+ messages in thread
From: Muli Ben-Yehuda @ 2008-05-11 10:08 UTC (permalink / raw)
  To: FUJITA Tomonori; +Cc: alexisb, linux-kernel

On Fri, May 09, 2008 at 09:23:29AM +0900, FUJITA Tomonori wrote:

> Can we put a pointer to dma_ops in struct device (archdata) like
> POWER does? The way to setup and handle x86 IOMMUs seems to become
> hacky day by day.

Per-device dma-ops will be useful for KVM (for pass-through device
support) and IB as well.

Cheers,
Muli

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2008-05-11 10:05 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-05-08 21:40 [RFC][PATCH] x86 calgary: add fallback dma_ops]] Alexis Bruemmer
2008-05-08 23:13 ` FUJITA Tomonori
2008-05-08 23:41   ` Alexis Bruemmer
2008-05-09  0:23     ` FUJITA Tomonori
2008-05-11 10:08       ` Muli Ben-Yehuda

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox