From: Sumit Semwal <sumit.semwal@linaro.org> To: linux-kernel@vger.kernel.org Cc: linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, linaro-kernel@lists.linaro.org, Benjamin Gaignard <benjamin.gaignard@linaro.org>, Sumit Semwal <sumit.semwal@linaro.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org> Subject: [RFC 4/4] cenalloc: a sample allocator for contiguous page allocation Date: Sat, 11 Oct 2014 01:37:58 +0530 [thread overview] Message-ID: <1412971678-4457-5-git-send-email-sumit.semwal@linaro.org> (raw) In-Reply-To: <1412971678-4457-1-git-send-email-sumit.semwal@linaro.org> From: Benjamin Gaignard <benjamin.gaignard@linaro.org> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> Cc: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: linux-media@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linaro-mm-sig@lists.linaro.org --- drivers/cenalloc/Makefile | 2 +- drivers/cenalloc/cenalloc_system_contig.c | 225 ++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 drivers/cenalloc/cenalloc_system_contig.c diff --git a/drivers/cenalloc/Makefile b/drivers/cenalloc/Makefile index d36b531..2f69b61 100644 --- a/drivers/cenalloc/Makefile +++ b/drivers/cenalloc/Makefile @@ -1,3 +1,3 @@ # Makefile for the cenalloc helper -obj-y += cenalloc.o +obj-y += cenalloc.o cenalloc_system_contig.o diff --git a/drivers/cenalloc/cenalloc_system_contig.c b/drivers/cenalloc/cenalloc_system_contig.c new file mode 100644 index 0000000..ecf0c03 --- /dev/null +++ b/drivers/cenalloc/cenalloc_system_contig.c @@ -0,0 +1,225 @@ +/* + * central allocator using kmalloc + * + * Copyright(C) 2014 Linaro Limited. All rights reserved. + * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <asm/page.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/highmem.h> +#include <linux/mm.h> +#include <linux/scatterlist.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include "cenalloc_priv.h" + +static gfp_t low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN); + +void cenalloc_pages_sync_for_device(struct device *dev, struct page *page, + size_t size, enum dma_data_direction dir) +{ + struct scatterlist sg; + + sg_init_table(&sg, 1); + sg_set_page(&sg, page, size, 0); + /* + * This is not correct - sg_dma_address needs a dma_addr_t that is valid + * for the the targeted device, but this works on the currently targeted + * hardware. + */ + sg_dma_address(&sg) = page_to_phys(page); + dma_sync_sg_for_device(dev, &sg, 1, dir); +} + +static int cenalloc_system_contig_allocate(struct cenalloc_allocator *allocator, + struct cenalloc_buffer *buffer, unsigned long len, + unsigned long align, unsigned long flags) +{ + int order = get_order(len); + struct page *page; + struct sg_table *table; + unsigned long i; + int ret; + + if (align > (PAGE_SIZE << order)) + return -EINVAL; + + page = alloc_pages(low_order_gfp_flags, order); + if (!page) + return -ENOMEM; + + split_page(page, order); + + len = PAGE_ALIGN(len); + for (i = len >> PAGE_SHIFT; i < (1 << order); i++) + __free_page(page + i); + + table = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!table) { + ret = -ENOMEM; + goto free_pages; + } + + ret = sg_alloc_table(table, 1, GFP_KERNEL); + if (ret) + goto free_table; + + sg_set_page(table->sgl, page, len, 0); + + buffer->sg_table = table; + + cenalloc_pages_sync_for_device(NULL, page, len, DMA_BIDIRECTIONAL); + + return 0; + +free_table: + kfree(table); +free_pages: + for (i = 0; i < len >> PAGE_SHIFT; i++) + __free_page(page + i); + + return ret; + +} + +static void cenalloc_system_contig_free(struct cenalloc_buffer *buffer) +{ + struct sg_table *table = buffer->sg_table; + struct page *page = sg_page(table->sgl); + unsigned long pages = PAGE_ALIGN(buffer->size) >> PAGE_SHIFT; + unsigned long i; + + for (i = 0; i < pages; i++) + __free_page(page + i); + sg_free_table(table); + kfree(table); +} + +static struct sg_table *cenalloc_system_contig_map_dma + (struct cenalloc_allocator *allocator, struct cenalloc_buffer *buffer) +{ + return buffer->sg_table; +} + +static void cenalloc_system_contig_unmap_dma + (struct cenalloc_allocator *allocator, struct cenalloc_buffer *buffer) +{ + +} + +static void *cenalloc_system_contig_map_kernel + (struct cenalloc_allocator *allocator, struct cenalloc_buffer *buffer) +{ + struct scatterlist *sg; + int i, j; + void *vaddr; + pgprot_t pgprot; + struct sg_table *table = buffer->sg_table; + int npages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; + struct page **pages = vmalloc(sizeof(struct page *) * npages); + struct page **tmp = pages; + + if (!pages) + return NULL; + + pgprot = pgprot_writecombine(PAGE_KERNEL); + + for_each_sg(table->sgl, sg, table->nents, i) { + int npages_this_entry = PAGE_ALIGN(sg->length) / PAGE_SIZE; + struct page *page = sg_page(sg); + + BUG_ON(i >= npages); + for (j = 0; j < npages_this_entry; j++) + *(tmp++) = page++; + } + vaddr = vmap(pages, npages, VM_MAP, pgprot); + vfree(pages); + + if (vaddr == NULL) + return ERR_PTR(-ENOMEM); + + return vaddr; +} + +static void cenalloc_system_contig_unmap_kernel + (struct cenalloc_allocator *allocator, struct cenalloc_buffer *buffer) +{ + vunmap(buffer->vaddr); +} + +static int cenalloc_system_contig_map_user + (struct cenalloc_allocator *mapper, struct cenalloc_buffer *buffer, + struct vm_area_struct *vma) +{ + struct sg_table *table = buffer->sg_table; + unsigned long addr = vma->vm_start; + unsigned long offset = vma->vm_pgoff * PAGE_SIZE; + struct scatterlist *sg; + int i; + int ret; + + for_each_sg(table->sgl, sg, table->nents, i) { + struct page *page = sg_page(sg); + unsigned long remainder = vma->vm_end - addr; + unsigned long len = sg->length; + + if (offset >= sg->length) { + offset -= sg->length; + continue; + } else if (offset) { + page += offset / PAGE_SIZE; + len = sg->length - offset; + offset = 0; + } + len = min(len, remainder); + ret = remap_pfn_range(vma, addr, page_to_pfn(page), len, + vma->vm_page_prot); + if (ret) + return ret; + addr += len; + if (addr >= vma->vm_end) + return 0; + } + return 0; +} + +static void cenalloc_system_contig_sync_for_device + (struct cenalloc_allocator *allocator, struct cenalloc_buffer *buffer, + struct device *dev, enum dma_data_direction dir) +{ + /* TODO */ +} + +static struct cenalloc_allocator_ops system_contig_ops = { + .allocate = cenalloc_system_contig_allocate, + .free = cenalloc_system_contig_free, + .map_dma = cenalloc_system_contig_map_dma, + .unmap_dma = cenalloc_system_contig_unmap_dma, + .map_kernel = cenalloc_system_contig_map_kernel, + .unmap_kernel = cenalloc_system_contig_unmap_kernel, + .map_user = cenalloc_system_contig_map_user, + .sync_for_device = cenalloc_system_contig_sync_for_device, +}; + +struct cenalloc_allocator system_contig = { + .ops = &system_contig_ops, + .type = CENALLOC_ALLOCATOR_SYSTEM, + .id = CENALLOC_ALLOCATOR_SYSTEM, + .name = "system contiguous memory allocator", +}; -- 1.9.1
WARNING: multiple messages have this Message-ID (diff)
From: Sumit Semwal <sumit.semwal@linaro.org> To: linux-kernel@vger.kernel.org Cc: linaro-kernel@lists.linaro.org, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Benjamin Gaignard <benjamin.gaignard@linaro.org>, linux-media@vger.kernel.org Subject: [RFC 4/4] cenalloc: a sample allocator for contiguous page allocation Date: Sat, 11 Oct 2014 01:37:58 +0530 [thread overview] Message-ID: <1412971678-4457-5-git-send-email-sumit.semwal@linaro.org> (raw) In-Reply-To: <1412971678-4457-1-git-send-email-sumit.semwal@linaro.org> From: Benjamin Gaignard <benjamin.gaignard@linaro.org> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> Cc: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: linux-media@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linaro-mm-sig@lists.linaro.org --- drivers/cenalloc/Makefile | 2 +- drivers/cenalloc/cenalloc_system_contig.c | 225 ++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 drivers/cenalloc/cenalloc_system_contig.c diff --git a/drivers/cenalloc/Makefile b/drivers/cenalloc/Makefile index d36b531..2f69b61 100644 --- a/drivers/cenalloc/Makefile +++ b/drivers/cenalloc/Makefile @@ -1,3 +1,3 @@ # Makefile for the cenalloc helper -obj-y += cenalloc.o +obj-y += cenalloc.o cenalloc_system_contig.o diff --git a/drivers/cenalloc/cenalloc_system_contig.c b/drivers/cenalloc/cenalloc_system_contig.c new file mode 100644 index 0000000..ecf0c03 --- /dev/null +++ b/drivers/cenalloc/cenalloc_system_contig.c @@ -0,0 +1,225 @@ +/* + * central allocator using kmalloc + * + * Copyright(C) 2014 Linaro Limited. All rights reserved. + * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <asm/page.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/highmem.h> +#include <linux/mm.h> +#include <linux/scatterlist.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include "cenalloc_priv.h" + +static gfp_t low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN); + +void cenalloc_pages_sync_for_device(struct device *dev, struct page *page, + size_t size, enum dma_data_direction dir) +{ + struct scatterlist sg; + + sg_init_table(&sg, 1); + sg_set_page(&sg, page, size, 0); + /* + * This is not correct - sg_dma_address needs a dma_addr_t that is valid + * for the the targeted device, but this works on the currently targeted + * hardware. + */ + sg_dma_address(&sg) = page_to_phys(page); + dma_sync_sg_for_device(dev, &sg, 1, dir); +} + +static int cenalloc_system_contig_allocate(struct cenalloc_allocator *allocator, + struct cenalloc_buffer *buffer, unsigned long len, + unsigned long align, unsigned long flags) +{ + int order = get_order(len); + struct page *page; + struct sg_table *table; + unsigned long i; + int ret; + + if (align > (PAGE_SIZE << order)) + return -EINVAL; + + page = alloc_pages(low_order_gfp_flags, order); + if (!page) + return -ENOMEM; + + split_page(page, order); + + len = PAGE_ALIGN(len); + for (i = len >> PAGE_SHIFT; i < (1 << order); i++) + __free_page(page + i); + + table = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!table) { + ret = -ENOMEM; + goto free_pages; + } + + ret = sg_alloc_table(table, 1, GFP_KERNEL); + if (ret) + goto free_table; + + sg_set_page(table->sgl, page, len, 0); + + buffer->sg_table = table; + + cenalloc_pages_sync_for_device(NULL, page, len, DMA_BIDIRECTIONAL); + + return 0; + +free_table: + kfree(table); +free_pages: + for (i = 0; i < len >> PAGE_SHIFT; i++) + __free_page(page + i); + + return ret; + +} + +static void cenalloc_system_contig_free(struct cenalloc_buffer *buffer) +{ + struct sg_table *table = buffer->sg_table; + struct page *page = sg_page(table->sgl); + unsigned long pages = PAGE_ALIGN(buffer->size) >> PAGE_SHIFT; + unsigned long i; + + for (i = 0; i < pages; i++) + __free_page(page + i); + sg_free_table(table); + kfree(table); +} + +static struct sg_table *cenalloc_system_contig_map_dma + (struct cenalloc_allocator *allocator, struct cenalloc_buffer *buffer) +{ + return buffer->sg_table; +} + +static void cenalloc_system_contig_unmap_dma + (struct cenalloc_allocator *allocator, struct cenalloc_buffer *buffer) +{ + +} + +static void *cenalloc_system_contig_map_kernel + (struct cenalloc_allocator *allocator, struct cenalloc_buffer *buffer) +{ + struct scatterlist *sg; + int i, j; + void *vaddr; + pgprot_t pgprot; + struct sg_table *table = buffer->sg_table; + int npages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; + struct page **pages = vmalloc(sizeof(struct page *) * npages); + struct page **tmp = pages; + + if (!pages) + return NULL; + + pgprot = pgprot_writecombine(PAGE_KERNEL); + + for_each_sg(table->sgl, sg, table->nents, i) { + int npages_this_entry = PAGE_ALIGN(sg->length) / PAGE_SIZE; + struct page *page = sg_page(sg); + + BUG_ON(i >= npages); + for (j = 0; j < npages_this_entry; j++) + *(tmp++) = page++; + } + vaddr = vmap(pages, npages, VM_MAP, pgprot); + vfree(pages); + + if (vaddr == NULL) + return ERR_PTR(-ENOMEM); + + return vaddr; +} + +static void cenalloc_system_contig_unmap_kernel + (struct cenalloc_allocator *allocator, struct cenalloc_buffer *buffer) +{ + vunmap(buffer->vaddr); +} + +static int cenalloc_system_contig_map_user + (struct cenalloc_allocator *mapper, struct cenalloc_buffer *buffer, + struct vm_area_struct *vma) +{ + struct sg_table *table = buffer->sg_table; + unsigned long addr = vma->vm_start; + unsigned long offset = vma->vm_pgoff * PAGE_SIZE; + struct scatterlist *sg; + int i; + int ret; + + for_each_sg(table->sgl, sg, table->nents, i) { + struct page *page = sg_page(sg); + unsigned long remainder = vma->vm_end - addr; + unsigned long len = sg->length; + + if (offset >= sg->length) { + offset -= sg->length; + continue; + } else if (offset) { + page += offset / PAGE_SIZE; + len = sg->length - offset; + offset = 0; + } + len = min(len, remainder); + ret = remap_pfn_range(vma, addr, page_to_pfn(page), len, + vma->vm_page_prot); + if (ret) + return ret; + addr += len; + if (addr >= vma->vm_end) + return 0; + } + return 0; +} + +static void cenalloc_system_contig_sync_for_device + (struct cenalloc_allocator *allocator, struct cenalloc_buffer *buffer, + struct device *dev, enum dma_data_direction dir) +{ + /* TODO */ +} + +static struct cenalloc_allocator_ops system_contig_ops = { + .allocate = cenalloc_system_contig_allocate, + .free = cenalloc_system_contig_free, + .map_dma = cenalloc_system_contig_map_dma, + .unmap_dma = cenalloc_system_contig_unmap_dma, + .map_kernel = cenalloc_system_contig_map_kernel, + .unmap_kernel = cenalloc_system_contig_unmap_kernel, + .map_user = cenalloc_system_contig_map_user, + .sync_for_device = cenalloc_system_contig_sync_for_device, +}; + +struct cenalloc_allocator system_contig = { + .ops = &system_contig_ops, + .type = CENALLOC_ALLOCATOR_SYSTEM, + .id = CENALLOC_ALLOCATOR_SYSTEM, + .name = "system contiguous memory allocator", +}; -- 1.9.1
next prev parent reply other threads:[~2014-10-10 20:09 UTC|newest] Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top 2014-10-10 20:07 [RFC 0/4] dma-buf Constraints-Enabled Allocation helpers Sumit Semwal 2014-10-10 20:07 ` Sumit Semwal 2014-10-10 20:07 ` [RFC 1/4] dma-buf: Add constraints sharing information Sumit Semwal 2014-10-10 20:07 ` Sumit Semwal 2014-10-11 18:55 ` Daniel Vetter 2014-10-11 18:55 ` Daniel Vetter 2014-10-13 8:14 ` [Linaro-mm-sig] " Laura Abbott 2014-10-13 9:32 ` Daniel Vetter 2014-10-13 9:32 ` Daniel Vetter 2014-12-10 13:31 ` Sumit Semwal 2014-12-10 13:31 ` Sumit Semwal 2014-12-10 13:47 ` Daniel Vetter 2014-12-10 13:47 ` Daniel Vetter 2015-01-08 14:14 ` [Linaro-mm-sig] " Benjamin Gaignard 2014-10-10 20:07 ` [RFC 2/4] cenalloc: Constraint-Enabled Allocation helpers for dma-buf Sumit Semwal 2014-10-10 20:07 ` Sumit Semwal 2014-10-10 23:09 ` Greg Kroah-Hartman 2014-10-10 23:09 ` Greg Kroah-Hartman 2014-10-11 18:40 ` Daniel Vetter 2014-10-11 18:40 ` Daniel Vetter 2014-10-14 14:03 ` Sumit Semwal 2014-10-14 14:03 ` Sumit Semwal 2014-10-13 8:35 ` [Linaro-mm-sig] " Laura Abbott 2014-10-14 14:11 ` Sumit Semwal 2014-10-10 20:07 ` [RFC 3/4] cenalloc: Build files for constraint-enabled allocation helpers Sumit Semwal 2014-10-10 20:07 ` Sumit Semwal [this message] 2014-10-10 20:07 ` [RFC 4/4] cenalloc: a sample allocator for contiguous page allocation Sumit Semwal 2014-10-13 8:12 ` [Linaro-mm-sig] [RFC 0/4] dma-buf Constraints-Enabled Allocation helpers Laura Abbott 2014-10-14 14:00 ` Sumit Semwal 2014-10-14 14:00 ` Sumit Semwal
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1412971678-4457-5-git-send-email-sumit.semwal@linaro.org \ --to=sumit.semwal@linaro.org \ --cc=benjamin.gaignard@linaro.org \ --cc=dri-devel@lists.freedesktop.org \ --cc=gregkh@linuxfoundation.org \ --cc=linaro-kernel@lists.linaro.org \ --cc=linaro-mm-sig@lists.linaro.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-media@vger.kernel.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.