* [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
* [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 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
* [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
* [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
* [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
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