public inbox for linux-arm-kernel@lists.infradead.org
 help / color / mirror / Atom feed
* [PATCH 0/2] ARM: dma-mapping: add highmem support for coherent allocation
@ 2013-01-16 15:31 Marek Szyprowski
  2013-01-16 15:31 ` [PATCH 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone Marek Szyprowski
  2013-01-16 15:31 ` [PATCH 2/2] ARM: dma-mapping: use himem for DMA buffers for IOMMU-mapped devices Marek Szyprowski
  0 siblings, 2 replies; 12+ messages in thread
From: Marek Szyprowski @ 2013-01-16 15:31 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

This is the last missing piece to let us efficiently use large DMA
buffers on systems with lots of memory, which have support for himem
enabled. The first patch adds support for CMA regions placed in high
memory zones, the second one also enables allocations of individual
pages from high memory zone for IOMMU-mapped devices. Those two changes
let us to significantly save low memory for other tasks.

Best regards
Marek Szyprowski
Samsung Poland R&D Center


Patch summary:

Marek Szyprowski (2):
  ARM: dma-mapping: add support for CMA regions placed in highmem zone
  ARM: dma-mapping: use himem for DMA buffers for IOMMU-mapped devices

 arch/arm/mm/dma-mapping.c |   70 +++++++++++++++++++++++++++++++++------------
 1 file changed, 52 insertions(+), 18 deletions(-)

-- 
1.7.9.5

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

* [PATCH 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone
  2013-01-16 15:31 [PATCH 0/2] ARM: dma-mapping: add highmem support for coherent allocation Marek Szyprowski
@ 2013-01-16 15:31 ` Marek Szyprowski
  2013-02-04 13:23   ` [PATCHv2 " Marek Szyprowski
  2013-01-16 15:31 ` [PATCH 2/2] ARM: dma-mapping: use himem for DMA buffers for IOMMU-mapped devices Marek Szyprowski
  1 sibling, 1 reply; 12+ messages in thread
From: Marek Szyprowski @ 2013-01-16 15:31 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds missing pieces to correctly support memory pages served
from CMA regions placed in high memory zones. Please note that the default
global CMA area is still put into lowmem and is limited by optional
architecture specific DMA zone. One can however put device specific CMA
regions in high memory zone to reduce lowmem usage.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 arch/arm/mm/dma-mapping.c |   61 +++++++++++++++++++++++++++++++++------------
 1 file changed, 45 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 6b2fb87..4080c37 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -186,16 +186,29 @@ static u64 get_coherent_dma_mask(struct device *dev)
 
 static void __dma_clear_buffer(struct page *page, size_t size)
 {
-	void *ptr;
 	/*
 	 * Ensure that the allocated pages are zeroed, and that any data
 	 * lurking in the kernel direct-mapped region is invalidated.
 	 */
-	ptr = page_address(page);
-	if (ptr) {
-		memset(ptr, 0, size);
-		dmac_flush_range(ptr, ptr + size);
-		outer_flush_range(__pa(ptr), __pa(ptr) + size);
+	if (!PageHighMem(page)) {
+		void *ptr = page_address(page);
+		if (ptr) {
+			memset(ptr, 0, size);
+			dmac_flush_range(ptr, ptr + size);
+			outer_flush_range(__pa(ptr), __pa(ptr) + size);
+		}
+	} else {
+		phys_addr_t base = __pfn_to_phys(page_to_pfn(page));
+		phys_addr_t end = base + size;
+		while (size > 0) {
+			void *ptr = kmap_atomic(page);
+			memset(ptr, 0, PAGE_SIZE);
+			dmac_flush_range(ptr, ptr + PAGE_SIZE);
+			kunmap_atomic(ptr);
+			page++;
+			size -= PAGE_SIZE;
+		}
+		outer_flush_range(base, end);
 	}
 }
 
@@ -243,7 +256,8 @@ static void __dma_free_buffer(struct page *page, size_t size)
 #endif
 
 static void *__alloc_from_contiguous(struct device *dev, size_t size,
-				     pgprot_t prot, struct page **ret_page);
+				     pgprot_t prot, struct page **ret_page,
+				     const void *caller);
 
 static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp,
 				 pgprot_t prot, struct page **ret_page,
@@ -346,10 +360,11 @@ static int __init atomic_pool_init(void)
 		goto no_pages;
 
 	if (IS_ENABLED(CONFIG_CMA))
-		ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page);
+		ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page,
+					      atomic_pool_init);
 	else
 		ptr = __alloc_remap_buffer(NULL, pool->size, GFP_KERNEL, prot,
-					   &page, NULL);
+					   &page, atomic_pool_init);
 	if (ptr) {
 		int i;
 
@@ -542,27 +557,41 @@ static int __free_from_pool(void *start, size_t size)
 }
 
 static void *__alloc_from_contiguous(struct device *dev, size_t size,
-				     pgprot_t prot, struct page **ret_page)
+				     pgprot_t prot, struct page **ret_page,
+				     const void *caller)
 {
 	unsigned long order = get_order(size);
 	size_t count = size >> PAGE_SHIFT;
 	struct page *page;
+	void *ptr;
 
 	page = dma_alloc_from_contiguous(dev, count, order);
 	if (!page)
 		return NULL;
 
 	__dma_clear_buffer(page, size);
-	__dma_remap(page, size, prot);
 
+	if (!PageHighMem(page)) {
+		__dma_remap(page, size, prot);
+		ptr = page_address(page);
+	} else {
+		ptr = __dma_alloc_remap(page, size, GFP_KERNEL, prot, caller);
+		if (!ptr) {
+			dma_release_from_contiguous(dev, page, count);
+			return NULL;
+		}
+	}
 	*ret_page = page;
-	return page_address(page);
+	return ptr;
 }
 
 static void __free_from_contiguous(struct device *dev, struct page *page,
-				   size_t size)
+				   void *cpu_addr, size_t size)
 {
-	__dma_remap(page, size, pgprot_kernel);
+	if (!PageHighMem(page))
+		__dma_remap(page, size, pgprot_kernel);
+	else
+		__dma_free_remap(cpu_addr, size);
 	dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT);
 }
 
@@ -645,7 +674,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
 	else if (!IS_ENABLED(CONFIG_CMA))
 		addr = __alloc_remap_buffer(dev, size, gfp, prot, &page, caller);
 	else
-		addr = __alloc_from_contiguous(dev, size, prot, &page);
+		addr = __alloc_from_contiguous(dev, size, prot, &page, caller);
 
 	if (addr)
 		*handle = pfn_to_dma(dev, page_to_pfn(page));
@@ -739,7 +768,7 @@ static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
 		 * Non-atomic allocations cannot be freed with IRQs disabled
 		 */
 		WARN_ON(irqs_disabled());
-		__free_from_contiguous(dev, page, size);
+		__free_from_contiguous(dev, page, cpu_addr, size);
 	}
 }
 
-- 
1.7.9.5

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

* [PATCH 2/2] ARM: dma-mapping: use himem for DMA buffers for IOMMU-mapped devices
  2013-01-16 15:31 [PATCH 0/2] ARM: dma-mapping: add highmem support for coherent allocation Marek Szyprowski
  2013-01-16 15:31 ` [PATCH 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone Marek Szyprowski
@ 2013-01-16 15:31 ` Marek Szyprowski
  1 sibling, 0 replies; 12+ messages in thread
From: Marek Szyprowski @ 2013-01-16 15:31 UTC (permalink / raw)
  To: linux-arm-kernel

IOMMU can provide access to any memory page, so there is no point in
limiting the allocated pages only to lowmem, once other parts of
dma-mapping subsystem correctly supports himem pages.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 arch/arm/mm/dma-mapping.c |    9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 4080c37..9a6c8ce 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1095,12 +1095,17 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
 		return pages;
 	}
 
+	/*
+	 * IOMMU can map any pages, so himem can also be used here
+	 */
+	gfp |= __GFP_NOWARN | __GFP_HIGHMEM;
+
 	while (count) {
 		int j, order = __fls(count);
 
-		pages[i] = alloc_pages(gfp | __GFP_NOWARN, order);
+		pages[i] = alloc_pages(gfp, order);
 		while (!pages[i] && order)
-			pages[i] = alloc_pages(gfp | __GFP_NOWARN, --order);
+			pages[i] = alloc_pages(gfp, --order);
 		if (!pages[i])
 			goto error;
 
-- 
1.7.9.5

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

* [PATCHv2 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone
  2013-01-16 15:31 ` [PATCH 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone Marek Szyprowski
@ 2013-02-04 13:23   ` Marek Szyprowski
  2013-02-04 13:51     ` Michal Nazarewicz
                       ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Marek Szyprowski @ 2013-02-04 13:23 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds missing pieces to correctly support memory pages served
from CMA regions placed in high memory zones. Please note that the default
global CMA area is still put into lowmem and is limited by optional
architecture specific DMA zone. One can however put device specific CMA
regions in high memory zone to reduce lowmem usage.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---

Changle log: 
v2: restructured code and made all himem checks positive 
    ('if (PageHighMem(page))' instead of 'if (!PageHighMem(page))')
---
 arch/arm/mm/dma-mapping.c |   53 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 40 insertions(+), 13 deletions(-)

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 076c26d..90e059b 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -186,13 +186,24 @@ static u64 get_coherent_dma_mask(struct device *dev)
 
 static void __dma_clear_buffer(struct page *page, size_t size)
 {
-	void *ptr;
 	/*
 	 * Ensure that the allocated pages are zeroed, and that any data
 	 * lurking in the kernel direct-mapped region is invalidated.
 	 */
-	ptr = page_address(page);
-	if (ptr) {
+	if (PageHighMem(page)) {
+		phys_addr_t base = __pfn_to_phys(page_to_pfn(page));
+		phys_addr_t end = base + size;
+		while (size > 0) {
+			void *ptr = kmap_atomic(page);
+			memset(ptr, 0, PAGE_SIZE);
+			dmac_flush_range(ptr, ptr + PAGE_SIZE);
+			kunmap_atomic(ptr);
+			page++;
+			size -= PAGE_SIZE;
+		}
+		outer_flush_range(base, end);
+	} else {
+		void *ptr = page_address(page);
 		memset(ptr, 0, size);
 		dmac_flush_range(ptr, ptr + size);
 		outer_flush_range(__pa(ptr), __pa(ptr) + size);
@@ -243,7 +254,8 @@ static void __dma_free_buffer(struct page *page, size_t size)
 #endif
 
 static void *__alloc_from_contiguous(struct device *dev, size_t size,
-				     pgprot_t prot, struct page **ret_page);
+				     pgprot_t prot, struct page **ret_page,
+				     const void *caller);
 
 static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp,
 				 pgprot_t prot, struct page **ret_page,
@@ -346,10 +358,11 @@ static int __init atomic_pool_init(void)
 		goto no_pages;
 
 	if (IS_ENABLED(CONFIG_CMA))
-		ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page);
+		ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page,
+					      atomic_pool_init);
 	else
 		ptr = __alloc_remap_buffer(NULL, pool->size, GFP_KERNEL, prot,
-					   &page, NULL);
+					   &page, atomic_pool_init);
 	if (ptr) {
 		int i;
 
@@ -542,27 +555,41 @@ static int __free_from_pool(void *start, size_t size)
 }
 
 static void *__alloc_from_contiguous(struct device *dev, size_t size,
-				     pgprot_t prot, struct page **ret_page)
+				     pgprot_t prot, struct page **ret_page,
+				     const void *caller)
 {
 	unsigned long order = get_order(size);
 	size_t count = size >> PAGE_SHIFT;
 	struct page *page;
+	void *ptr;
 
 	page = dma_alloc_from_contiguous(dev, count, order);
 	if (!page)
 		return NULL;
 
 	__dma_clear_buffer(page, size);
-	__dma_remap(page, size, prot);
 
+	if (PageHighMem(page)) {
+		ptr = __dma_alloc_remap(page, size, GFP_KERNEL, prot, caller);
+		if (!ptr) {
+			dma_release_from_contiguous(dev, page, count);
+			return NULL;
+		}
+	} else {
+		__dma_remap(page, size, prot);
+		ptr = page_address(page);
+	}
 	*ret_page = page;
-	return page_address(page);
+	return ptr;
 }
 
 static void __free_from_contiguous(struct device *dev, struct page *page,
-				   size_t size)
+				   void *cpu_addr, size_t size)
 {
-	__dma_remap(page, size, pgprot_kernel);
+	if (PageHighMem(page))
+		__dma_free_remap(cpu_addr, size);
+	else
+		__dma_remap(page, size, pgprot_kernel);
 	dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT);
 }
 
@@ -645,7 +672,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
 	else if (!IS_ENABLED(CONFIG_CMA))
 		addr = __alloc_remap_buffer(dev, size, gfp, prot, &page, caller);
 	else
-		addr = __alloc_from_contiguous(dev, size, prot, &page);
+		addr = __alloc_from_contiguous(dev, size, prot, &page, caller);
 
 	if (addr)
 		*handle = pfn_to_dma(dev, page_to_pfn(page));
@@ -739,7 +766,7 @@ static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
 		 * Non-atomic allocations cannot be freed with IRQs disabled
 		 */
 		WARN_ON(irqs_disabled());
-		__free_from_contiguous(dev, page, size);
+		__free_from_contiguous(dev, page, cpu_addr, size);
 	}
 }
 
-- 
1.7.9.5

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

* [PATCHv2 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone
  2013-02-04 13:23   ` [PATCHv2 " Marek Szyprowski
@ 2013-02-04 13:51     ` Michal Nazarewicz
  2013-02-04 14:10       ` Russell King - ARM Linux
  2013-02-04 14:41     ` Michal Nazarewicz
  2013-02-07 14:59     ` [PATCHv3 " Marek Szyprowski
  2 siblings, 1 reply; 12+ messages in thread
From: Michal Nazarewicz @ 2013-02-04 13:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 04 2013, Marek Szyprowski wrote:
> @@ -186,13 +186,24 @@ static u64 get_coherent_dma_mask(struct device *dev)
>  
>  static void __dma_clear_buffer(struct page *page, size_t size)
>  {
> -	void *ptr;
>  	/*
>  	 * Ensure that the allocated pages are zeroed, and that any data
>  	 * lurking in the kernel direct-mapped region is invalidated.
>  	 */
> -	ptr = page_address(page);
> -	if (ptr) {
> +	if (PageHighMem(page)) {
> +		phys_addr_t base = __pfn_to_phys(page_to_pfn(page));
> +		phys_addr_t end = base + size;
> +		while (size > 0) {
> +			void *ptr = kmap_atomic(page);
> +			memset(ptr, 0, PAGE_SIZE);
> +			dmac_flush_range(ptr, ptr + PAGE_SIZE);
> +			kunmap_atomic(ptr);
> +			page++;
> +			size -= PAGE_SIZE;
> +		}
> +		outer_flush_range(base, end);
> +	} else {
> +		void *ptr = page_address(page);

There used to be a ?if (ptr)? check which is now missing.  Why is that?

>  		memset(ptr, 0, size);
>  		dmac_flush_range(ptr, ptr + size);
>  		outer_flush_range(__pa(ptr), __pa(ptr) + size);

-- 
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Micha? ?mina86? Nazarewicz    (o o)
ooo +----<email/xmpp: mpn@google.com>--------------ooO--(_)--Ooo--
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130204/c8c84ff1/attachment.sig>

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

* [PATCHv2 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone
  2013-02-04 13:51     ` Michal Nazarewicz
@ 2013-02-04 14:10       ` Russell King - ARM Linux
  2013-02-04 14:24         ` Michal Nazarewicz
  0 siblings, 1 reply; 12+ messages in thread
From: Russell King - ARM Linux @ 2013-02-04 14:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 04, 2013 at 02:51:52PM +0100, Michal Nazarewicz wrote:
> On Mon, Feb 04 2013, Marek Szyprowski wrote:
> > @@ -186,13 +186,24 @@ static u64 get_coherent_dma_mask(struct device *dev)
> >  
> >  static void __dma_clear_buffer(struct page *page, size_t size)
> >  {
> > -	void *ptr;
> >  	/*
> >  	 * Ensure that the allocated pages are zeroed, and that any data
> >  	 * lurking in the kernel direct-mapped region is invalidated.
> >  	 */
> > -	ptr = page_address(page);
> > -	if (ptr) {
> > +	if (PageHighMem(page)) {
> > +		phys_addr_t base = __pfn_to_phys(page_to_pfn(page));
> > +		phys_addr_t end = base + size;
> > +		while (size > 0) {
> > +			void *ptr = kmap_atomic(page);
> > +			memset(ptr, 0, PAGE_SIZE);
> > +			dmac_flush_range(ptr, ptr + PAGE_SIZE);
> > +			kunmap_atomic(ptr);
> > +			page++;
> > +			size -= PAGE_SIZE;
> > +		}
> > +		outer_flush_range(base, end);
> > +	} else {
> > +		void *ptr = page_address(page);
> 
> There used to be a ?if (ptr)? check which is now missing.  Why is that?

Because lowmem pages always have an address.

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

* [PATCHv2 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone
  2013-02-04 14:10       ` Russell King - ARM Linux
@ 2013-02-04 14:24         ` Michal Nazarewicz
  2013-02-04 14:34           ` Russell King - ARM Linux
  0 siblings, 1 reply; 12+ messages in thread
From: Michal Nazarewicz @ 2013-02-04 14:24 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 04 2013, Russell King - ARM Linux wrote:
> On Mon, Feb 04, 2013 at 02:51:52PM +0100, Michal Nazarewicz wrote:
>> On Mon, Feb 04 2013, Marek Szyprowski wrote:
>> > @@ -186,13 +186,24 @@ static u64 get_coherent_dma_mask(struct device *dev)
>> >  
>> >  static void __dma_clear_buffer(struct page *page, size_t size)
>> >  {
>> > -	void *ptr;
>> >  	/*
>> >  	 * Ensure that the allocated pages are zeroed, and that any data
>> >  	 * lurking in the kernel direct-mapped region is invalidated.
>> >  	 */
>> > -	ptr = page_address(page);
>> > -	if (ptr) {
>> > +	if (PageHighMem(page)) {
>> > +		phys_addr_t base = __pfn_to_phys(page_to_pfn(page));
>> > +		phys_addr_t end = base + size;
>> > +		while (size > 0) {
>> > +			void *ptr = kmap_atomic(page);
>> > +			memset(ptr, 0, PAGE_SIZE);
>> > +			dmac_flush_range(ptr, ptr + PAGE_SIZE);
>> > +			kunmap_atomic(ptr);
>> > +			page++;
>> > +			size -= PAGE_SIZE;
>> > +		}
>> > +		outer_flush_range(base, end);
>> > +	} else {
>> > +		void *ptr = page_address(page);
>> 
>> There used to be a ?if (ptr)? check which is now missing.  Why is that?
>
> Because lowmem pages always have an address.

Perhaps it should use lowmem_page_address() then?

-- 
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Micha? ?mina86? Nazarewicz    (o o)
ooo +----<email/xmpp: mpn@google.com>--------------ooO--(_)--Ooo--
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130204/3e4952c3/attachment.sig>

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

* [PATCHv2 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone
  2013-02-04 14:24         ` Michal Nazarewicz
@ 2013-02-04 14:34           ` Russell King - ARM Linux
  2013-02-04 14:42             ` Michal Nazarewicz
  0 siblings, 1 reply; 12+ messages in thread
From: Russell King - ARM Linux @ 2013-02-04 14:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 04, 2013 at 03:24:51PM +0100, Michal Nazarewicz wrote:
> On Mon, Feb 04 2013, Russell King - ARM Linux wrote:
> > On Mon, Feb 04, 2013 at 02:51:52PM +0100, Michal Nazarewicz wrote:
> >> On Mon, Feb 04 2013, Marek Szyprowski wrote:
> >> > @@ -186,13 +186,24 @@ static u64 get_coherent_dma_mask(struct device *dev)
> >> >  
> >> >  static void __dma_clear_buffer(struct page *page, size_t size)
> >> >  {
> >> > -	void *ptr;
> >> >  	/*
> >> >  	 * Ensure that the allocated pages are zeroed, and that any data
> >> >  	 * lurking in the kernel direct-mapped region is invalidated.
> >> >  	 */
> >> > -	ptr = page_address(page);
> >> > -	if (ptr) {
> >> > +	if (PageHighMem(page)) {
> >> > +		phys_addr_t base = __pfn_to_phys(page_to_pfn(page));
> >> > +		phys_addr_t end = base + size;
> >> > +		while (size > 0) {
> >> > +			void *ptr = kmap_atomic(page);
> >> > +			memset(ptr, 0, PAGE_SIZE);
> >> > +			dmac_flush_range(ptr, ptr + PAGE_SIZE);
> >> > +			kunmap_atomic(ptr);
> >> > +			page++;
> >> > +			size -= PAGE_SIZE;
> >> > +		}
> >> > +		outer_flush_range(base, end);
> >> > +	} else {
> >> > +		void *ptr = page_address(page);
> >> 
> >> There used to be a ?if (ptr)? check which is now missing.  Why is that?
> >
> > Because lowmem pages always have an address.
> 
> Perhaps it should use lowmem_page_address() then?

It's well defined that page_address() will be non-NULL for lowmem pages.
There is nothing wrong with the above.

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

* [PATCHv2 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone
  2013-02-04 13:23   ` [PATCHv2 " Marek Szyprowski
  2013-02-04 13:51     ` Michal Nazarewicz
@ 2013-02-04 14:41     ` Michal Nazarewicz
  2013-02-07 14:59     ` [PATCHv3 " Marek Szyprowski
  2 siblings, 0 replies; 12+ messages in thread
From: Michal Nazarewicz @ 2013-02-04 14:41 UTC (permalink / raw)
  To: linux-arm-kernel


On Mon, Feb 04 2013, Marek Szyprowski wrote:
> This patch adds missing pieces to correctly support memory pages served
> from CMA regions placed in high memory zones. Please note that the default
> global CMA area is still put into lowmem and is limited by optional
> architecture specific DMA zone. One can however put device specific CMA
> regions in high memory zone to reduce lowmem usage.
>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

Acked-by: Michal Nazarewicz <mina86@mina86.com>

-- 
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Micha? ?mina86? Nazarewicz    (o o)
ooo +----<email/xmpp: mpn@google.com>--------------ooO--(_)--Ooo--
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130204/025f50b8/attachment.sig>

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

* [PATCHv2 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone
  2013-02-04 14:34           ` Russell King - ARM Linux
@ 2013-02-04 14:42             ` Michal Nazarewicz
  2013-02-04 15:11               ` Russell King - ARM Linux
  0 siblings, 1 reply; 12+ messages in thread
From: Michal Nazarewicz @ 2013-02-04 14:42 UTC (permalink / raw)
  To: linux-arm-kernel

> On Mon, Feb 04, 2013 at 03:24:51PM +0100, Michal Nazarewicz wrote:
>> Perhaps it should use lowmem_page_address() then?

On Mon, Feb 04 2013, Russell King - ARM Linux wrote:
> It's well defined that page_address() will be non-NULL for lowmem pages.
> There is nothing wrong with the above.

It would save on a branch and a function call though.

-- 
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Micha? ?mina86? Nazarewicz    (o o)
ooo +----<email/xmpp: mpn@google.com>--------------ooO--(_)--Ooo--
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130204/4533538f/attachment-0001.sig>

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

* [PATCHv2 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone
  2013-02-04 14:42             ` Michal Nazarewicz
@ 2013-02-04 15:11               ` Russell King - ARM Linux
  0 siblings, 0 replies; 12+ messages in thread
From: Russell King - ARM Linux @ 2013-02-04 15:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 04, 2013 at 03:42:26PM +0100, Michal Nazarewicz wrote:
> > On Mon, Feb 04, 2013 at 03:24:51PM +0100, Michal Nazarewicz wrote:
> >> Perhaps it should use lowmem_page_address() then?
> 
> On Mon, Feb 04 2013, Russell King - ARM Linux wrote:
> > It's well defined that page_address() will be non-NULL for lowmem pages.
> > There is nothing wrong with the above.
> 
> It would save on a branch and a function call though.

Depending on the size of struct page, if we care that much, we can probably
enable WANT_PAGE_VIRTUAL which'll make it even cheaper as it's just a
dereference.

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

* [PATCHv3 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone
  2013-02-04 13:23   ` [PATCHv2 " Marek Szyprowski
  2013-02-04 13:51     ` Michal Nazarewicz
  2013-02-04 14:41     ` Michal Nazarewicz
@ 2013-02-07 14:59     ` Marek Szyprowski
  2 siblings, 0 replies; 12+ messages in thread
From: Marek Szyprowski @ 2013-02-07 14:59 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds missing pieces to correctly support memory pages served
from CMA regions placed in high memory zones. Please note that the default
global CMA area is still put into lowmem and is limited by optional
architecture specific DMA zone. One can however put device specific CMA
regions in high memory zone to reduce lowmem usage.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
---
Changle log: 
v3: fixed build break for non-MMU builds (thanks to Thierry Reding!)

v2: restructured code and made all himem checks positive 
    ('if (PageHighMem(page))' instead of 'if (!PageHighMem(page))')
---
 arch/arm/mm/dma-mapping.c |   57 +++++++++++++++++++++++++++++++++------------
 1 file changed, 42 insertions(+), 15 deletions(-)

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index de93ecd..5f79361 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -186,13 +186,24 @@ static u64 get_coherent_dma_mask(struct device *dev)
 
 static void __dma_clear_buffer(struct page *page, size_t size)
 {
-	void *ptr;
 	/*
 	 * Ensure that the allocated pages are zeroed, and that any data
 	 * lurking in the kernel direct-mapped region is invalidated.
 	 */
-	ptr = page_address(page);
-	if (ptr) {
+	if (PageHighMem(page)) {
+		phys_addr_t base = __pfn_to_phys(page_to_pfn(page));
+		phys_addr_t end = base + size;
+		while (size > 0) {
+			void *ptr = kmap_atomic(page);
+			memset(ptr, 0, PAGE_SIZE);
+			dmac_flush_range(ptr, ptr + PAGE_SIZE);
+			kunmap_atomic(ptr);
+			page++;
+			size -= PAGE_SIZE;
+		}
+		outer_flush_range(base, end);
+	} else {
+		void *ptr = page_address(page);
 		memset(ptr, 0, size);
 		dmac_flush_range(ptr, ptr + size);
 		outer_flush_range(__pa(ptr), __pa(ptr) + size);
@@ -243,7 +254,8 @@ static void __dma_free_buffer(struct page *page, size_t size)
 #endif
 
 static void *__alloc_from_contiguous(struct device *dev, size_t size,
-				     pgprot_t prot, struct page **ret_page);
+				     pgprot_t prot, struct page **ret_page,
+				     const void *caller);
 
 static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp,
 				 pgprot_t prot, struct page **ret_page,
@@ -346,10 +358,11 @@ static int __init atomic_pool_init(void)
 		goto no_pages;
 
 	if (IS_ENABLED(CONFIG_CMA))
-		ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page);
+		ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page,
+					      atomic_pool_init);
 	else
 		ptr = __alloc_remap_buffer(NULL, pool->size, GFP_KERNEL, prot,
-					   &page, NULL);
+					   &page, atomic_pool_init);
 	if (ptr) {
 		int i;
 
@@ -542,27 +555,41 @@ static int __free_from_pool(void *start, size_t size)
 }
 
 static void *__alloc_from_contiguous(struct device *dev, size_t size,
-				     pgprot_t prot, struct page **ret_page)
+				     pgprot_t prot, struct page **ret_page,
+				     const void *caller)
 {
 	unsigned long order = get_order(size);
 	size_t count = size >> PAGE_SHIFT;
 	struct page *page;
+	void *ptr;
 
 	page = dma_alloc_from_contiguous(dev, count, order);
 	if (!page)
 		return NULL;
 
 	__dma_clear_buffer(page, size);
-	__dma_remap(page, size, prot);
 
+	if (PageHighMem(page)) {
+		ptr = __dma_alloc_remap(page, size, GFP_KERNEL, prot, caller);
+		if (!ptr) {
+			dma_release_from_contiguous(dev, page, count);
+			return NULL;
+		}
+	} else {
+		__dma_remap(page, size, prot);
+		ptr = page_address(page);
+	}
 	*ret_page = page;
-	return page_address(page);
+	return ptr;
 }
 
 static void __free_from_contiguous(struct device *dev, struct page *page,
-				   size_t size)
+				   void *cpu_addr, size_t size)
 {
-	__dma_remap(page, size, pgprot_kernel);
+	if (PageHighMem(page))
+		__dma_free_remap(cpu_addr, size);
+	else
+		__dma_remap(page, size, pgprot_kernel);
 	dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT);
 }
 
@@ -583,9 +610,9 @@ static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot)
 #define __get_dma_pgprot(attrs, prot)	__pgprot(0)
 #define __alloc_remap_buffer(dev, size, gfp, prot, ret, c)	NULL
 #define __alloc_from_pool(size, ret_page)			NULL
-#define __alloc_from_contiguous(dev, size, prot, ret)		NULL
+#define __alloc_from_contiguous(dev, size, prot, ret, c)	NULL
 #define __free_from_pool(cpu_addr, size)			0
-#define __free_from_contiguous(dev, page, size)			do { } while (0)
+#define __free_from_contiguous(dev, page, cpu_addr, size)	do { } while (0)
 #define __dma_free_remap(cpu_addr, size)			do { } while (0)
 
 #endif	/* CONFIG_MMU */
@@ -645,7 +672,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
 	else if (!IS_ENABLED(CONFIG_CMA))
 		addr = __alloc_remap_buffer(dev, size, gfp, prot, &page, caller);
 	else
-		addr = __alloc_from_contiguous(dev, size, prot, &page);
+		addr = __alloc_from_contiguous(dev, size, prot, &page, caller);
 
 	if (addr)
 		*handle = pfn_to_dma(dev, page_to_pfn(page));
@@ -739,7 +766,7 @@ static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
 		 * Non-atomic allocations cannot be freed with IRQs disabled
 		 */
 		WARN_ON(irqs_disabled());
-		__free_from_contiguous(dev, page, size);
+		__free_from_contiguous(dev, page, cpu_addr, size);
 	}
 }
 
-- 
1.7.9.5

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

end of thread, other threads:[~2013-02-07 14:59 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-01-16 15:31 [PATCH 0/2] ARM: dma-mapping: add highmem support for coherent allocation Marek Szyprowski
2013-01-16 15:31 ` [PATCH 1/2] ARM: dma-mapping: add support for CMA regions placed in highmem zone Marek Szyprowski
2013-02-04 13:23   ` [PATCHv2 " Marek Szyprowski
2013-02-04 13:51     ` Michal Nazarewicz
2013-02-04 14:10       ` Russell King - ARM Linux
2013-02-04 14:24         ` Michal Nazarewicz
2013-02-04 14:34           ` Russell King - ARM Linux
2013-02-04 14:42             ` Michal Nazarewicz
2013-02-04 15:11               ` Russell King - ARM Linux
2013-02-04 14:41     ` Michal Nazarewicz
2013-02-07 14:59     ` [PATCHv3 " Marek Szyprowski
2013-01-16 15:31 ` [PATCH 2/2] ARM: dma-mapping: use himem for DMA buffers for IOMMU-mapped devices Marek Szyprowski

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