2008-06-12 08:26:30

by FUJITA Tomonori

[permalink] [raw]
Subject: [PATCH -mm] fix per-device dma_mapping_ops support

Andrew, can you put this patch to -mm?

This patch fixes a bug in per-device dma_mapping_ops support. The
patch to fix Calgary IOMMU with per-device dma_mapping_ops support is
ready so we need this fix in -mm.

http://lkml.org/lkml/2008/6/11/447

This is a resend of:

http://lkml.org/lkml/2008/6/3/430

=
From: FUJITA Tomonori <[email protected]>
Date: Thu, 12 Jun 2008 17:14:02 +0900
Subject: [PATCH] fix per-device dma_mapping_ops support

On x86, pci_dma_supported, pci_alloc_consistent, and
pci_free_consistent don't call DMA APIs directly (the majority of
platforms do). per-device dma_mapping_ops support patch needs to
modify pci-dma.c.

Signed-off-by: FUJITA Tomonori <[email protected]>
---
arch/x86/kernel/pci-dma.c | 25 +++++++++++++++----------
1 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c
index fa500c4..a2b98a5 100644
--- a/arch/x86/kernel/pci-dma.c
+++ b/arch/x86/kernel/pci-dma.c
@@ -318,6 +318,8 @@ static int dma_release_coherent(struct device *dev, int order, void *vaddr)

int dma_supported(struct device *dev, u64 mask)
{
+ struct dma_mapping_ops *ops = get_dma_ops(dev);
+
#ifdef CONFIG_PCI
if (mask > 0xffffffff && forbid_dac > 0) {
dev_info(dev, "PCI: Disallowing DAC for device\n");
@@ -325,8 +327,8 @@ int dma_supported(struct device *dev, u64 mask)
}
#endif

- if (dma_ops->dma_supported)
- return dma_ops->dma_supported(dev, mask);
+ if (ops->dma_supported)
+ return ops->dma_supported(dev, mask);

/* Copied from i386. Doesn't make much sense, because it will
only work for pci_alloc_coherent.
@@ -373,6 +375,7 @@ void *
dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t gfp)
{
+ struct dma_mapping_ops *ops = get_dma_ops(dev);
void *memory = NULL;
struct page *page;
unsigned long dma_mask = 0;
@@ -435,8 +438,8 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
/* Let low level make its own zone decisions */
gfp &= ~(GFP_DMA32|GFP_DMA);

- if (dma_ops->alloc_coherent)
- return dma_ops->alloc_coherent(dev, size,
+ if (ops->alloc_coherent)
+ return ops->alloc_coherent(dev, size,
dma_handle, gfp);
return NULL;
}
@@ -448,14 +451,14 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
}
}

- if (dma_ops->alloc_coherent) {
+ if (ops->alloc_coherent) {
free_pages((unsigned long)memory, get_order(size));
gfp &= ~(GFP_DMA|GFP_DMA32);
- return dma_ops->alloc_coherent(dev, size, dma_handle, gfp);
+ return ops->alloc_coherent(dev, size, dma_handle, gfp);
}

- if (dma_ops->map_simple) {
- *dma_handle = dma_ops->map_simple(dev, virt_to_phys(memory),
+ if (ops->map_simple) {
+ *dma_handle = ops->map_simple(dev, virt_to_phys(memory),
size,
PCI_DMA_BIDIRECTIONAL);
if (*dma_handle != bad_dma_address)
@@ -477,12 +480,14 @@ EXPORT_SYMBOL(dma_alloc_coherent);
void dma_free_coherent(struct device *dev, size_t size,
void *vaddr, dma_addr_t bus)
{
+ struct dma_mapping_ops *ops = get_dma_ops(dev);
+
int order = get_order(size);
WARN_ON(irqs_disabled()); /* for portability */
if (dma_release_coherent(dev, order, vaddr))
return;
- if (dma_ops->unmap_single)
- dma_ops->unmap_single(dev, bus, size, 0);
+ if (ops->unmap_single)
+ ops->unmap_single(dev, bus, size, 0);
free_pages((unsigned long)vaddr, order);
}
EXPORT_SYMBOL(dma_free_coherent);
--
1.5.5.GIT


2008-07-01 21:52:19

by Alexis Bruemmer

[permalink] [raw]
Subject: Re: [PATCH -mm] fix per-device dma_mapping_ops support

Where are we with with this patch-- as well as the needed calgary
update:

http://marc.info/?l=linux-kernel&m=121329005131176&w=2

Are they at a state where they can be picked-up? If not what is needed
to progress?

Thanks!

Alexis


On Thu, 2008-06-12 at 17:21 +0900, FUJITA Tomonori wrote:
> Andrew, can you put this patch to -mm?
>
> This patch fixes a bug in per-device dma_mapping_ops support. The
> patch to fix Calgary IOMMU with per-device dma_mapping_ops support is
> ready so we need this fix in -mm.
>
> http://lkml.org/lkml/2008/6/11/447
>
> This is a resend of:
>
> http://lkml.org/lkml/2008/6/3/430
>
> =
> From: FUJITA Tomonori <[email protected]>
> Date: Thu, 12 Jun 2008 17:14:02 +0900
> Subject: [PATCH] fix per-device dma_mapping_ops support
>
> On x86, pci_dma_supported, pci_alloc_consistent, and
> pci_free_consistent don't call DMA APIs directly (the majority of
> platforms do). per-device dma_mapping_ops support patch needs to
> modify pci-dma.c.
>
> Signed-off-by: FUJITA Tomonori <[email protected]>
> ---
> arch/x86/kernel/pci-dma.c | 25 +++++++++++++++----------
> 1 files changed, 15 insertions(+), 10 deletions(-)
>
> diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c
> index fa500c4..a2b98a5 100644
> --- a/arch/x86/kernel/pci-dma.c
> +++ b/arch/x86/kernel/pci-dma.c
> @@ -318,6 +318,8 @@ static int dma_release_coherent(struct device *dev, int order, void *vaddr)
>
> int dma_supported(struct device *dev, u64 mask)
> {
> + struct dma_mapping_ops *ops = get_dma_ops(dev);
> +
> #ifdef CONFIG_PCI
> if (mask > 0xffffffff && forbid_dac > 0) {
> dev_info(dev, "PCI: Disallowing DAC for device\n");
> @@ -325,8 +327,8 @@ int dma_supported(struct device *dev, u64 mask)
> }
> #endif
>
> - if (dma_ops->dma_supported)
> - return dma_ops->dma_supported(dev, mask);
> + if (ops->dma_supported)
> + return ops->dma_supported(dev, mask);
>
> /* Copied from i386. Doesn't make much sense, because it will
> only work for pci_alloc_coherent.
> @@ -373,6 +375,7 @@ void *
> dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
> gfp_t gfp)
> {
> + struct dma_mapping_ops *ops = get_dma_ops(dev);
> void *memory = NULL;
> struct page *page;
> unsigned long dma_mask = 0;
> @@ -435,8 +438,8 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
> /* Let low level make its own zone decisions */
> gfp &= ~(GFP_DMA32|GFP_DMA);
>
> - if (dma_ops->alloc_coherent)
> - return dma_ops->alloc_coherent(dev, size,
> + if (ops->alloc_coherent)
> + return ops->alloc_coherent(dev, size,
> dma_handle, gfp);
> return NULL;
> }
> @@ -448,14 +451,14 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
> }
> }
>
> - if (dma_ops->alloc_coherent) {
> + if (ops->alloc_coherent) {
> free_pages((unsigned long)memory, get_order(size));
> gfp &= ~(GFP_DMA|GFP_DMA32);
> - return dma_ops->alloc_coherent(dev, size, dma_handle, gfp);
> + return ops->alloc_coherent(dev, size, dma_handle, gfp);
> }
>
> - if (dma_ops->map_simple) {
> - *dma_handle = dma_ops->map_simple(dev, virt_to_phys(memory),
> + if (ops->map_simple) {
> + *dma_handle = ops->map_simple(dev, virt_to_phys(memory),
> size,
> PCI_DMA_BIDIRECTIONAL);
> if (*dma_handle != bad_dma_address)
> @@ -477,12 +480,14 @@ EXPORT_SYMBOL(dma_alloc_coherent);
> void dma_free_coherent(struct device *dev, size_t size,
> void *vaddr, dma_addr_t bus)
> {
> + struct dma_mapping_ops *ops = get_dma_ops(dev);
> +
> int order = get_order(size);
> WARN_ON(irqs_disabled()); /* for portability */
> if (dma_release_coherent(dev, order, vaddr))
> return;
> - if (dma_ops->unmap_single)
> - dma_ops->unmap_single(dev, bus, size, 0);
> + if (ops->unmap_single)
> + ops->unmap_single(dev, bus, size, 0);
> free_pages((unsigned long)vaddr, order);
> }
> EXPORT_SYMBOL(dma_free_coherent);

2008-07-02 02:45:11

by FUJITA Tomonori

[permalink] [raw]
Subject: Re: [PATCH -mm] fix per-device dma_mapping_ops support

On Tue, 01 Jul 2008 14:52:04 -0700
Alexis Bruemmer <[email protected]> wrote:

> Where are we with with this patch-- as well as the needed calgary
> update:

This patch has been in the -mm:

dma-mapping-x86-per-device-dma_mapping_ops-support-fix-2.patch

I hope that the dma-mapping-per-device patchset gets into mainline in
2.6.27.


> http://marc.info/?l=linux-kernel&m=121329005131176&w=2
>
> Are they at a state where they can be picked-up? If not what is needed
> to progress?

The updated patch to fix the Calgary IOMMU driver looks fine to
me. Andrew, can you put it in the -mm?

Thanks,

2008-07-02 02:50:24

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH -mm] fix per-device dma_mapping_ops support

On Wed, 2 Jul 2008 11:37:27 +0900 FUJITA Tomonori <[email protected]> wrote:

> > http://marc.info/?l=linux-kernel&m=121329005131176&w=2
> >
> > Are they at a state where they can be picked-up? If not what is needed
> > to progress?
>
> The updated patch to fix the Calgary IOMMU driver looks fine to
> me. Andrew, can you put it in the -mm?

umm, put what into -mm? I see four different patches and a string of
back-and-forth discussion with no clear testing results. It is
unreasonsable and too error-prone to have me go picking through such a
thread attempting to work out which of those patches should be applied
and then writing people's changelogs for them.

2008-07-02 03:52:18

by FUJITA Tomonori

[permalink] [raw]
Subject: Re: [PATCH -mm] fix per-device dma_mapping_ops support

On Tue, 1 Jul 2008 19:48:40 -0700
Andrew Morton <[email protected]> wrote:

> On Wed, 2 Jul 2008 11:37:27 +0900 FUJITA Tomonori <[email protected]> wrote:
>
> > > http://marc.info/?l=linux-kernel&m=121329005131176&w=2
> > >
> > > Are they at a state where they can be picked-up? If not what is needed
> > > to progress?
> >
> > The updated patch to fix the Calgary IOMMU driver looks fine to
> > me. Andrew, can you put it in the -mm?
>
> umm, put what into -mm? I see four different patches and a string of
> back-and-forth discussion with no clear testing results. It is
> unreasonsable and too error-prone to have me go picking through such a
> thread attempting to work out which of those patches should be applied
> and then writing people's changelogs for them.

Sorry,

My initial patch has some bugs (wasn't tested because I don't have
Calgary hardware) but Alexis fixed the problems and submitted new one
(he successfully tested it). I had one minor comment about it then he
submitted another one.

http://marc.info/?l=linux-kernel&m=121329005131176&w=2

I think that it can get into mainline with the dma-mapping-per-device
patchset.

As we discussed, we could do better but it takes some time. I think
that it's better to fix Calgary problems now. Then I'll try to improve
dma-mapping-per-device stuff.

Here's a repost of Alexis's latest patch.

Thanks,

=
From: Alexis Bruemmer <[email protected]>
Subject: [PATCH] x86 calgary: fix handling of devces that aren't behind the Calgary

The calgary code can give drivers addresses above 4GB which is very
bad for hardware that is only 32bit DMA addressable.

With this patch, the calgary code sets the global dma_ops to swiotlb
or nommu properly, and the dma_ops of devices behind the
Calgary/CalIOC2 to calgary_dma_ops. So the calgary code can handle
devices safely that aren't behind the Calgary/CalIOC2.

Signed-off-by: Alexis Bruemmer <[email protected]>
Signed-off-by: FUJITA Tomonori <[email protected]>
---
arch/x86/kernel/pci-calgary_64.c | 72 ++++++++++++++-----------------------
include/asm-x86/iommu.h | 1 +
2 files changed, 28 insertions(+), 45 deletions(-)

diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c
index dca9a82..84a8d6b 100644
--- a/arch/x86/kernel/pci-calgary_64.c
+++ b/arch/x86/kernel/pci-calgary_64.c
@@ -36,6 +36,7 @@
#include <linux/delay.h>
#include <linux/scatterlist.h>
#include <linux/iommu-helper.h>
+#include <asm/iommu.h>
#include <asm/gart.h>
#include <asm/calgary.h>
#include <asm/tce.h>
@@ -410,22 +411,6 @@ static void calgary_unmap_sg(struct device *dev,
}
}

-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)
{
@@ -436,9 +421,6 @@ static int calgary_map_sg(struct device *dev, struct scatterlist *sg,
unsigned long entry;
int i;

- if (!translation_enabled(tbl))
- return calgary_nontranslate_map_sg(dev, sg, nelems, direction);
-
for_each_sg(sg, s, nelems, i) {
BUG_ON(!sg_page(s));

@@ -474,7 +456,6 @@ error:
static dma_addr_t calgary_map_single(struct device *dev, phys_addr_t paddr,
size_t size, int direction)
{
- dma_addr_t dma_handle = bad_dma_address;
void *vaddr = phys_to_virt(paddr);
unsigned long uaddr;
unsigned int npages;
@@ -483,12 +464,7 @@ static dma_addr_t calgary_map_single(struct device *dev, phys_addr_t paddr,
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);
-
- return dma_handle;
+ return iommu_alloc(dev, tbl, vaddr, npages, direction);
}

static void calgary_unmap_single(struct device *dev, dma_addr_t dma_handle,
@@ -497,9 +473,6 @@ static void calgary_unmap_single(struct device *dev, dma_addr_t dma_handle,
struct iommu_table *tbl = find_iommu_table(dev);
unsigned int npages;

- if (!translation_enabled(tbl))
- return;
-
npages = num_dma_pages(dma_handle, size);
iommu_free(tbl, dma_handle, npages);
}
@@ -522,18 +495,12 @@ static void* calgary_alloc_coherent(struct device *dev, size_t size,
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;
-
free:
free_pages((unsigned long)ret, get_order(size));
ret = NULL;
@@ -1230,6 +1197,16 @@ static int __init calgary_init(void)
goto error;
} while (1);

+ dev = NULL;
+ for_each_pci_dev(dev) {
+ struct iommu_table *tbl;
+
+ tbl = find_iommu_table(&dev->dev);
+
+ if(translation_enabled(tbl))
+ dev->dev.archdata.dma_ops = &calgary_dma_ops;
+ }
+
return ret;

error:
@@ -1251,6 +1228,7 @@ error:
calgary_disable_translation(dev);
calgary_free_bus(dev);
pci_dev_put(dev); /* Undo calgary_init_one()'s pci_dev_get() */
+ dev->dev.archdata.dma_ops = NULL;
} while (1);

return ret;
@@ -1430,6 +1408,10 @@ void __init detect_calgary(void)
printk(KERN_INFO "PCI-DMA: Calgary TCE table spec is %d, "
"CONFIG_IOMMU_DEBUG is %s.\n", specified_table_size,
debugging ? "enabled" : "disabled");
+
+ /* swiotlb for devices that aren't behind the Calgary. */
+ if (end_pfn > MAX_DMA32_PFN)
+ swiotlb = 1;
}
return;

@@ -1446,7 +1428,7 @@ int __init calgary_iommu_init(void)
{
int ret;

- if (no_iommu || swiotlb)
+ if (no_iommu || (swiotlb && !calgary_detected))
return -ENODEV;

if (!calgary_detected)
@@ -1459,15 +1441,15 @@ int __init calgary_iommu_init(void)
if (ret) {
printk(KERN_ERR "PCI-DMA: Calgary init failed %d, "
"falling back to no_iommu\n", ret);
- if (end_pfn > MAX_DMA32_PFN)
- printk(KERN_ERR "WARNING more than 4GB of memory, "
- "32bit PCI may malfunction.\n");
return ret;
}

force_iommu = 1;
bad_dma_address = 0x0;
- dma_ops = &calgary_dma_ops;
+
+ /* dma_ops is set to swiotlb or nommu */
+ if (!dma_ops)
+ dma_ops = &nommu_dma_ops;

return 0;
}
diff --git a/include/asm-x86/iommu.h b/include/asm-x86/iommu.h
index 07862fd..1a5e7df 100644
--- a/include/asm-x86/iommu.h
+++ b/include/asm-x86/iommu.h
@@ -3,6 +3,7 @@

extern void pci_iommu_shutdown(void);
extern void no_iommu_init(void);
+extern struct dma_mapping_ops nommu_dma_ops;
extern int force_iommu, no_iommu;
extern int iommu_detected;
#ifdef CONFIG_IOMMU
--
1.5.5.GIT

2008-07-02 04:10:14

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH -mm] fix per-device dma_mapping_ops support

On Wed, 2 Jul 2008 12:43:39 +0900 FUJITA Tomonori <[email protected]> wrote:

> My initial patch has some bugs (wasn't tested because I don't have
> Calgary hardware) but Alexis fixed the problems and submitted new one
> (he successfully tested it). I had one minor comment about it then he
> submitted another one.
>
> http://marc.info/?l=linux-kernel&m=121329005131176&w=2
>
> I think that it can get into mainline with the dma-mapping-per-device
> patchset.
>
> As we discussed, we could do better but it takes some time. I think
> that it's better to fix Calgary problems now. Then I'll try to improve
> dma-mapping-per-device stuff.
>
> Here's a repost of Alexis's latest patch.
>
> Thanks,
>
> =
> From: Alexis Bruemmer <[email protected]>
> Subject: [PATCH] x86 calgary: fix handling of devces that aren't behind the Calgary
>
> The calgary code can give drivers addresses above 4GB which is very
> bad for hardware that is only 32bit DMA addressable.
>
> With this patch, the calgary code sets the global dma_ops to swiotlb
> or nommu properly, and the dma_ops of devices behind the
> Calgary/CalIOC2 to calgary_dma_ops. So the calgary code can handle
> devices safely that aren't behind the Calgary/CalIOC2.
>

OK.. Looks like this needs to be folded into
dma-mapping-x86-per-device-dma_mapping_ops-support prior to merging to
keep everything bisect-happy.

> + if(translation_enabled(tbl))

checkpatch?


btw, x86_64 allmodconfig says:

arch/x86/kernel/pci-calgary_64.c: In function 'build_detail_arrays':
arch/x86/kernel/pci-calgary_64.c:1263: warning: comparison is always false due t

which is

if (rio_table_hdr->num_scal_dev > MAX_NUMNODES){

so I'll cancel that order for a 512-node Calgary machine ;)

2008-07-02 04:33:19

by FUJITA Tomonori

[permalink] [raw]
Subject: Re: [PATCH -mm] fix per-device dma_mapping_ops support

On Tue, 1 Jul 2008 21:06:20 -0700
Andrew Morton <[email protected]> wrote:

> On Wed, 2 Jul 2008 12:43:39 +0900 FUJITA Tomonori <[email protected]> wrote:
>
> > My initial patch has some bugs (wasn't tested because I don't have
> > Calgary hardware) but Alexis fixed the problems and submitted new one
> > (he successfully tested it). I had one minor comment about it then he
> > submitted another one.
> >
> > http://marc.info/?l=linux-kernel&m=121329005131176&w=2
> >
> > I think that it can get into mainline with the dma-mapping-per-device
> > patchset.
> >
> > As we discussed, we could do better but it takes some time. I think
> > that it's better to fix Calgary problems now. Then I'll try to improve
> > dma-mapping-per-device stuff.
> >
> > Here's a repost of Alexis's latest patch.
> >
> > Thanks,
> >
> > =
> > From: Alexis Bruemmer <[email protected]>
> > Subject: [PATCH] x86 calgary: fix handling of devces that aren't behind the Calgary
> >
> > The calgary code can give drivers addresses above 4GB which is very
> > bad for hardware that is only 32bit DMA addressable.
> >
> > With this patch, the calgary code sets the global dma_ops to swiotlb
> > or nommu properly, and the dma_ops of devices behind the
> > Calgary/CalIOC2 to calgary_dma_ops. So the calgary code can handle
> > devices safely that aren't behind the Calgary/CalIOC2.
> >
>
> OK.. Looks like this needs to be folded into
> dma-mapping-x86-per-device-dma_mapping_ops-support prior to merging to
> keep everything bisect-happy.

This doesn't need to be fold though it could.

This patch uses per-device-dma_mapping_ops support so this need to be
applied after the per-device-dma_mapping_ops patchset.


> > + if(translation_enabled(tbl))
>
> checkpatch?

Oops, sorry about that.

2008-07-02 04:48:26

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH -mm] fix per-device dma_mapping_ops support

On Wed, 2 Jul 2008 13:29:19 +0900 FUJITA Tomonori <[email protected]> wrote:

> > OK.. Looks like this needs to be folded into
> > dma-mapping-x86-per-device-dma_mapping_ops-support prior to merging to
> > keep everything bisect-happy.
>
> This doesn't need to be fold though it could.
>
> This patch uses per-device-dma_mapping_ops support so this need to be
> applied after the per-device-dma_mapping_ops patchset.

OK, fine, let's keep them separate commits then.