2012-08-29 06:55:59

by Hiroshi Doyu

[permalink] [raw]
Subject: [RFC 0/5] ARM: dma-mapping: New dma_map_ops to control IOVA more precisely

Hi,

The following APIs are needed for us to support the legacy Tegra
memory manager for devices("NvMap") with *DMA mapping API*.

New API:

->iova_alloc(): To allocate IOVA area.
->iova_alloc_at(): To allocate IOVA area at specific address.
->iova_free(): To free IOVA area.

->map_page_at(): To map page at specific IOVA.

misc:
->iova_get_free_total(): To return how much IOVA is available totally.
->iova_get_free_max(): To return the size of biggest IOVA area.

Although NvMap itself will be replaced soon, there are cases for the
above API where we need to specify IOVA explicitly.

(1) HWAs may require the address for special purpose, like reset vector.
(2) IOVA linear mapping: ex: [RFC 5/5] ARM: dma-mapping: Introduce
dma_map_linear_attrs() for IOVA linear map
(3) To support different heaps. To have allocation and mapping
independently.

Some of them could be supported with creating different mappings, but
currently a device can have a single contiguous mapping, and we cannot
specifiy any address inside of a map since all IOVA alloction is done
implicitly now.

This is the revised version of:

http://lists.linaro.org/pipermail/linaro-mm-sig/2012-May/001947.html
http://lists.linaro.org/pipermail/linaro-mm-sig/2012-May/001948.html
http://lists.linaro.org/pipermail/linaro-mm-sig/2012-May/001949.html

Any comment would be really appreciated.

Hiroshi Doyu (5):
ARM: dma-mapping: New dma_map_ops->iova_get_free_{total,max}
functions
ARM: dma-mapping: New dma_map_ops->iova_{alloc,free}() functions
ARM: dma-mapping: New dma_map_ops->iova_alloc*_at* function
ARM: dma-mapping: New dma_map_ops->map_page*_at* function
ARM: dma-mapping: Introduce dma_map_linear_attrs() for IOVA linear
map

arch/arm/include/asm/dma-mapping.h | 55 +++++++++++++
arch/arm/mm/dma-mapping.c | 124 ++++++++++++++++++++++++++++++
include/asm-generic/dma-mapping-common.h | 20 +++++
include/linux/dma-mapping.h | 14 ++++
4 files changed, 213 insertions(+), 0 deletions(-)

--
1.7.5.4


2012-08-29 06:56:11

by Hiroshi Doyu

[permalink] [raw]
Subject: [RFC 1/5] ARM: dma-mapping: New dma_map_ops->iova_get_free_{total,max} functions

->iova>_get_free_total() returns the sum of available free areas.
->iova>_get_free_max() returns the largest available free area size.

Signed-off-by: Hiroshi Doyu <[email protected]>
---
arch/arm/include/asm/dma-mapping.h | 16 ++++++++++
arch/arm/mm/dma-mapping.c | 54 ++++++++++++++++++++++++++++++++++++
include/linux/dma-mapping.h | 3 ++
3 files changed, 73 insertions(+), 0 deletions(-)

diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index 2300484..1cbd279 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -170,6 +170,22 @@ static inline void dma_free_attrs(struct device *dev, size_t size,
ops->free(dev, size, cpu_addr, dma_handle, attrs);
}

+static inline size_t dma_iova_get_free_total(struct device *dev)
+{
+ struct dma_map_ops *ops = get_dma_ops(dev);
+ BUG_ON(!ops);
+
+ return ops->iova_get_free_total(dev);
+}
+
+static inline size_t dma_iova_get_free_max(struct device *dev)
+{
+ struct dma_map_ops *ops = get_dma_ops(dev);
+ BUG_ON(!ops);
+
+ return ops->iova_get_free_max(dev);
+}
+
/**
* arm_dma_mmap - map a coherent DMA allocation into user space
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index e4746b7..db17338 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1001,6 +1001,57 @@ fs_initcall(dma_debug_do_init);

/* IOMMU */

+static size_t arm_iommu_iova_get_free_total(struct device *dev)
+{
+ struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+ unsigned long flags;
+ size_t size = 0;
+ unsigned long start = 0;
+
+ BUG_ON(!dev);
+ BUG_ON(!mapping);
+
+ spin_lock_irqsave(&mapping->lock, flags);
+ while (1) {
+ unsigned long end;
+
+ start = bitmap_find_next_zero_area(mapping->bitmap,
+ mapping->bits, start, 1, 0);
+ if (start > mapping->bits)
+ break;
+
+ end = find_next_bit(mapping->bitmap, mapping->bits, start);
+ size += end - start;
+ start = end;
+ }
+ spin_unlock_irqrestore(&mapping->lock, flags);
+ return size << (mapping->order + PAGE_SHIFT);
+}
+
+static size_t arm_iommu_iova_get_free_max(struct device *dev)
+{
+ struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+ unsigned long flags;
+ size_t max_free = 0;
+ unsigned long start = 0;
+
+ spin_lock_irqsave(&mapping->lock, flags);
+ while (1) {
+ unsigned long end;
+
+ start = bitmap_find_next_zero_area(mapping->bitmap,
+ mapping->bits, start, 1, 0);
+ if (start > mapping->bits)
+ break;
+
+ end = find_next_bit(mapping->bitmap, mapping->bits, start);
+ max_free = max_t(size_t, max_free, end - start);
+ start = end;
+ }
+ spin_unlock_irqrestore(&mapping->lock, flags);
+ return max_free << (mapping->order + PAGE_SHIFT);
+}
+
static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping,
size_t size)
{
@@ -1721,6 +1772,9 @@ struct dma_map_ops iommu_ops = {
.unmap_sg = arm_iommu_unmap_sg,
.sync_sg_for_cpu = arm_iommu_sync_sg_for_cpu,
.sync_sg_for_device = arm_iommu_sync_sg_for_device,
+
+ .iova_get_free_total = arm_iommu_iova_get_free_total,
+ .iova_get_free_max = arm_iommu_iova_get_free_max,
};

struct dma_map_ops iommu_coherent_ops = {
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 94af418..0337182 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -53,6 +53,9 @@ struct dma_map_ops {
#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK
u64 (*get_required_mask)(struct device *dev);
#endif
+ size_t (*iova_get_free_total)(struct device *dev);
+ size_t (*iova_get_free_max)(struct device *dev);
+
int is_phys;
};

--
1.7.5.4

2012-08-29 06:56:26

by Hiroshi Doyu

[permalink] [raw]
Subject: [RFC 5/5] ARM: dma-mapping: Introduce dma_map_linear_attrs() for IOVA linear map

Introduce a helper function, dma_map_linear(_attrs)() to create IOVA
linear map, where IOVA and kernel virtual addresses are mapped at the
same address linearly. This is useful to support legacy device drivers
which expects no IOMMU.

Signed-off-by: Hiroshi Doyu <[email protected]>
---
arch/arm/include/asm/dma-mapping.h | 13 +++++++++++++
include/asm-generic/dma-mapping-common.h | 1 +
2 files changed, 14 insertions(+), 0 deletions(-)

diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index f04a533..7a78dd4 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -212,6 +212,19 @@ static inline size_t dma_iova_get_free_max(struct device *dev)
return ops->iova_get_free_max(dev);
}

+static inline dma_addr_t dma_map_linear_attrs(struct device *dev, void *va,
+ size_t size, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ dma_addr_t da;
+
+ da = dma_iova_alloc_at(dev, (dma_addr_t)va, size);
+ if (da == DMA_ERROR_CODE)
+ return DMA_ERROR_CODE;
+
+ return dma_map_single_at_attrs(dev, va, da, size, dir, attrs);
+}
+
/**
* arm_dma_mmap - map a coherent DMA allocation into user space
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
diff --git a/include/asm-generic/dma-mapping-common.h b/include/asm-generic/dma-mapping-common.h
index eada2d8..4564bf0 100644
--- a/include/asm-generic/dma-mapping-common.h
+++ b/include/asm-generic/dma-mapping-common.h
@@ -191,6 +191,7 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, NULL)
#define dma_map_single_at(d, a, h, s, r) \
dma_map_single_at_attrs(d, a, h, s, r, NULL)
+#define dma_map_linear(d, a, s, r) dma_map_linear_attrs(d, a, s, r, NULL)
#define dma_unmap_single(d, a, s, r) dma_unmap_single_attrs(d, a, s, r, NULL)
#define dma_map_sg(d, s, n, r) dma_map_sg_attrs(d, s, n, r, NULL)
#define dma_unmap_sg(d, s, n, r) dma_unmap_sg_attrs(d, s, n, r, NULL)
--
1.7.5.4

2012-08-29 06:56:27

by Hiroshi Doyu

[permalink] [raw]
Subject: [RFC 3/5] ARM: dma-mapping: New dma_map_ops->iova_alloc*_at* function

To allocate IOVA area at specified address

Signed-off-by: Hiroshi Doyu <[email protected]>
---
arch/arm/include/asm/dma-mapping.h | 9 +++++++++
arch/arm/mm/dma-mapping.c | 35 +++++++++++++++++++++++++++++++++++
include/linux/dma-mapping.h | 2 ++
3 files changed, 46 insertions(+), 0 deletions(-)

diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index 5b86600..f04a533 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -187,6 +187,15 @@ static inline void dma_iova_free(struct device *dev, dma_addr_t addr,
ops->iova_free(dev, addr, size);
}

+static inline dma_addr_t dma_iova_alloc_at(struct device *dev, dma_addr_t addr,
+ size_t size)
+{
+ struct dma_map_ops *ops = get_dma_ops(dev);
+ BUG_ON(!ops);
+
+ return ops->iova_alloc_at(dev, addr, size);
+}
+
static inline size_t dma_iova_get_free_total(struct device *dev)
{
struct dma_map_ops *ops = get_dma_ops(dev);
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index c18522a..8ca2d1a 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1080,6 +1080,40 @@ static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping,
return mapping->base + (start << (mapping->order + PAGE_SHIFT));
}

+static dma_addr_t __alloc_iova_at(struct dma_iommu_mapping *mapping,
+ dma_addr_t iova, size_t size)
+{
+ unsigned int count, start, orig;
+ unsigned long flags;
+
+ count = ((PAGE_ALIGN(size) >> PAGE_SHIFT) +
+ (1 << mapping->order) - 1) >> mapping->order;
+
+ spin_lock_irqsave(&mapping->lock, flags);
+
+ orig = (iova - mapping->base) >> (mapping->order + PAGE_SHIFT);
+ start = bitmap_find_next_zero_area(mapping->bitmap, mapping->bits,
+ orig, count, 0);
+
+ if ((start > mapping->bits) || (orig != start)) {
+ spin_unlock_irqrestore(&mapping->lock, flags);
+ return DMA_ERROR_CODE;
+ }
+
+ bitmap_set(mapping->bitmap, start, count);
+ spin_unlock_irqrestore(&mapping->lock, flags);
+
+ return mapping->base + (start << (mapping->order + PAGE_SHIFT));
+}
+
+static dma_addr_t arm_iommu_iova_alloc_at(struct device *dev, dma_addr_t iova,
+ size_t size)
+{
+ struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+
+ return __alloc_iova_at(mapping, iova, size);
+}
+
static dma_addr_t arm_iommu_iova_alloc(struct device *dev, size_t size)
{
struct dma_iommu_mapping *mapping = dev->archdata.mapping;
@@ -1789,6 +1823,7 @@ struct dma_map_ops iommu_ops = {
.sync_sg_for_device = arm_iommu_sync_sg_for_device,

.iova_alloc = arm_iommu_iova_alloc,
+ .iova_alloc_at = arm_iommu_iova_alloc_at,
.iova_free = arm_iommu_iova_free,
.iova_get_free_total = arm_iommu_iova_get_free_total,
.iova_get_free_max = arm_iommu_iova_get_free_max,
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index e85aa04..4cf4427 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -54,6 +54,8 @@ struct dma_map_ops {
u64 (*get_required_mask)(struct device *dev);
#endif
dma_addr_t (*iova_alloc)(struct device *dev, size_t size);
+ dma_addr_t (*iova_alloc_at)(struct device *dev, dma_addr_t dma_addr,
+ size_t size);
void (*iova_free)(struct device *dev, dma_addr_t addr, size_t size);
size_t (*iova_get_free_total)(struct device *dev);
size_t (*iova_get_free_max)(struct device *dev);
--
1.7.5.4

2012-08-29 06:56:47

by Hiroshi Doyu

[permalink] [raw]
Subject: [RFC 2/5] ARM: dma-mapping: New dma_map_ops->iova_{alloc,free}() functions

There are some cases that IOVA allocation and mapping have to be done
seperately, especially for perf optimization reasons. This patch
allows client modules to {alloc,free} IOVA space without backing up
actual pages for that area.

Signed-off-by: Hiroshi Doyu <[email protected]>
---
arch/arm/include/asm/dma-mapping.h | 17 +++++++++++++++++
arch/arm/mm/dma-mapping.c | 17 +++++++++++++++++
include/linux/dma-mapping.h | 2 ++
3 files changed, 36 insertions(+), 0 deletions(-)

diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index 1cbd279..5b86600 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -170,6 +170,23 @@ static inline void dma_free_attrs(struct device *dev, size_t size,
ops->free(dev, size, cpu_addr, dma_handle, attrs);
}

+static inline dma_addr_t dma_iova_alloc(struct device *dev, size_t size)
+{
+ struct dma_map_ops *ops = get_dma_ops(dev);
+ BUG_ON(!ops);
+
+ return ops->iova_alloc(dev, size);
+}
+
+static inline void dma_iova_free(struct device *dev, dma_addr_t addr,
+ size_t size)
+{
+ struct dma_map_ops *ops = get_dma_ops(dev);
+ BUG_ON(!ops);
+
+ ops->iova_free(dev, addr, size);
+}
+
static inline size_t dma_iova_get_free_total(struct device *dev)
{
struct dma_map_ops *ops = get_dma_ops(dev);
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index db17338..c18522a 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1080,6 +1080,13 @@ static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping,
return mapping->base + (start << (mapping->order + PAGE_SHIFT));
}

+static dma_addr_t arm_iommu_iova_alloc(struct device *dev, size_t size)
+{
+ struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+
+ return __alloc_iova(mapping, size);
+}
+
static inline void __free_iova(struct dma_iommu_mapping *mapping,
dma_addr_t addr, size_t size)
{
@@ -1094,6 +1101,14 @@ static inline void __free_iova(struct dma_iommu_mapping *mapping,
spin_unlock_irqrestore(&mapping->lock, flags);
}

+static void arm_iommu_iova_free(struct device *dev, dma_addr_t addr,
+ size_t size)
+{
+ struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+
+ __free_iova(mapping, addr, size);
+}
+
static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, gfp_t gfp)
{
struct page **pages;
@@ -1773,6 +1788,8 @@ struct dma_map_ops iommu_ops = {
.sync_sg_for_cpu = arm_iommu_sync_sg_for_cpu,
.sync_sg_for_device = arm_iommu_sync_sg_for_device,

+ .iova_alloc = arm_iommu_iova_alloc,
+ .iova_free = arm_iommu_iova_free,
.iova_get_free_total = arm_iommu_iova_get_free_total,
.iova_get_free_max = arm_iommu_iova_get_free_max,
};
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 0337182..e85aa04 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -53,6 +53,8 @@ struct dma_map_ops {
#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK
u64 (*get_required_mask)(struct device *dev);
#endif
+ dma_addr_t (*iova_alloc)(struct device *dev, size_t size);
+ void (*iova_free)(struct device *dev, dma_addr_t addr, size_t size);
size_t (*iova_get_free_total)(struct device *dev);
size_t (*iova_get_free_max)(struct device *dev);

--
1.7.5.4

2012-08-29 06:57:22

by Hiroshi Doyu

[permalink] [raw]
Subject: [RFC 4/5] ARM: dma-mapping: New dma_map_ops->map_page*_at* function

Signed-off-by: Hiroshi Doyu <[email protected]>
---
arch/arm/mm/dma-mapping.c | 18 ++++++++++++++++++
include/asm-generic/dma-mapping-common.h | 19 +++++++++++++++++++
include/linux/dma-mapping.h | 7 +++++++
3 files changed, 44 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 8ca2d1a..242289f 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1715,6 +1715,23 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
return arm_coherent_iommu_map_page(dev, page, offset, size, dir, attrs);
}

+static dma_addr_t arm_iommu_map_page_at(struct device *dev, struct page *page,
+ dma_addr_t dma_addr, unsigned long offset, size_t size,
+ enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+ int ret, len = PAGE_ALIGN(size + offset);
+
+ if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+ __dma_page_cpu_to_dev(page, offset, size, dir);
+
+ ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len, 0);
+ if (ret < 0)
+ return DMA_ERROR_CODE;
+
+ return dma_addr + offset;
+}
+
/**
* arm_coherent_iommu_unmap_page
* @dev: valid struct device pointer
@@ -1813,6 +1830,7 @@ struct dma_map_ops iommu_ops = {
.get_sgtable = arm_iommu_get_sgtable,

.map_page = arm_iommu_map_page,
+ .map_page_at = arm_iommu_map_page_at,
.unmap_page = arm_iommu_unmap_page,
.sync_single_for_cpu = arm_iommu_sync_single_for_cpu,
.sync_single_for_device = arm_iommu_sync_single_for_device,
diff --git a/include/asm-generic/dma-mapping-common.h b/include/asm-generic/dma-mapping-common.h
index de8bf89..eada2d8 100644
--- a/include/asm-generic/dma-mapping-common.h
+++ b/include/asm-generic/dma-mapping-common.h
@@ -26,6 +26,23 @@ static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
return addr;
}

+static inline dma_addr_t dma_map_single_at_attrs(struct device *dev, void *ptr,
+ dma_addr_t handle,
+ size_t size,
+ enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ struct dma_map_ops *ops = get_dma_ops(dev);
+ dma_addr_t addr;
+
+ kmemcheck_mark_initialized(ptr, size);
+ BUG_ON(!valid_dma_direction(dir));
+ addr = ops->map_page_at(dev, virt_to_page(ptr), handle,
+ (unsigned long)ptr & ~PAGE_MASK, size,
+ dir, attrs);
+ return addr;
+}
+
static inline void dma_unmap_single_attrs(struct device *dev, dma_addr_t addr,
size_t size,
enum dma_data_direction dir,
@@ -172,6 +189,8 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
}

#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, NULL)
+#define dma_map_single_at(d, a, h, s, r) \
+ dma_map_single_at_attrs(d, a, h, s, r, NULL)
#define dma_unmap_single(d, a, s, r) dma_unmap_single_attrs(d, a, s, r, NULL)
#define dma_map_sg(d, s, n, r) dma_map_sg_attrs(d, s, n, r, NULL)
#define dma_unmap_sg(d, s, n, r) dma_unmap_sg_attrs(d, s, n, r, NULL)
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 4cf4427..218bee3 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -25,6 +25,13 @@ struct dma_map_ops {
unsigned long offset, size_t size,
enum dma_data_direction dir,
struct dma_attrs *attrs);
+
+ dma_addr_t (*map_page_at)(struct device *dev, struct page *page,
+ dma_addr_t dma_handle,
+ unsigned long offset, size_t size,
+ enum dma_data_direction dir,
+ struct dma_attrs *attrs);
+
void (*unmap_page)(struct device *dev, dma_addr_t dma_handle,
size_t size, enum dma_data_direction dir,
struct dma_attrs *attrs);
--
1.7.5.4