2017-01-17 19:38 GMT+01:00 Laura Abbott labbott@redhat.com:
On 01/16/2017 01:47 AM, Benjamin Gaignard wrote:
2017-01-13 20:30 GMT+01:00 Laura Abbott labbott@redhat.com:
On 01/12/2017 06:01 AM, Benjamin Gaignard wrote:
This patch add allocator for CMA default region
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org
drivers/simpleallocator/Kconfig | 7 ++ drivers/simpleallocator/Makefile | 1 + drivers/simpleallocator/simple-allocator-cma.c | 163 +++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 drivers/simpleallocator/simple-allocator-cma.c
diff --git a/drivers/simpleallocator/Kconfig b/drivers/simpleallocator/Kconfig index c6fc2e3..788fb0b 100644 --- a/drivers/simpleallocator/Kconfig +++ b/drivers/simpleallocator/Kconfig @@ -7,4 +7,11 @@ config SIMPLE_ALLOCATOR The Simple Allocator Framework adds an API to allocate and share memory in userland.
+config SIMPLE_ALLOCATOR_CMA
tristate "Simple Allocator CMA"
select SIMPLE_ALLOCATOR
depends on DMA_CMA
---help---
Select this option to enable Simple Allocator on CMA area.
endmenu diff --git a/drivers/simpleallocator/Makefile b/drivers/simpleallocator/Makefile index e27c6ad..4e11611 100644 --- a/drivers/simpleallocator/Makefile +++ b/drivers/simpleallocator/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_SIMPLE_ALLOCATOR) += simple-allocator.o +obj-$(CONFIG_SIMPLE_ALLOCATOR_CMA) += simple-allocator-cma.o diff --git a/drivers/simpleallocator/simple-allocator-cma.c b/drivers/simpleallocator/simple-allocator-cma.c new file mode 100644 index 0000000..c4f5767 --- /dev/null +++ b/drivers/simpleallocator/simple-allocator-cma.c @@ -0,0 +1,163 @@ +/*
- Copyright (C) Linaro 2016
- Author: Benjamin Gaignard benjamin.gaignard@linaro.org
- License terms: GNU General Public License (GPL)
- */
+#include <linux/module.h> +#include <linux/slab.h>
+#include "simple-allocator-priv.h"
+static struct sa_device *default_cma;
+struct sa_cma_buffer_info {
void *vaddr;
dma_addr_t handle;
size_t size;
struct device *dev;
+};
+static struct sg_table *sa_cma_map_dma_buf(struct dma_buf_attachment *attach,
enum dma_data_direction direction)
+{
struct dma_buf *dmabuf = attach->dmabuf;
struct sa_cma_buffer_info *info = dmabuf->priv;
struct sg_table *sgt;
int ret;
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt)
return NULL;
ret = dma_get_sgtable(info->dev, sgt, info->vaddr, info->handle,
info->size);
if (ret < 0)
goto out;
return sgt;
+out:
kfree(sgt);
return NULL;
+}
+static void sa_cma_unmap_dma_buf(struct dma_buf_attachment *attach,
struct sg_table *sgt,
enum dma_data_direction dir)
+{
kfree(sgt);
+}
+static int sa_cma_mmap_dma_buf(struct dma_buf *dmabuf,
struct vm_area_struct *vma)
+{
struct sa_cma_buffer_info *info = dmabuf->priv;
int ret;
ret = dma_mmap_wc(info->dev, vma, info->vaddr, info->handle,
vma->vm_end - vma->vm_start);
return ret;
+}
+static void sa_cma_release_dma_buf(struct dma_buf *dmabuf) +{
struct sa_cma_buffer_info *info = dmabuf->priv;
dma_free_wc(info->dev, info->size, info->vaddr, info->handle);
kfree(info);
+}
+static void *sa_cma_kmap_dma_buf(struct dma_buf *dmabuf, unsigned long offset) +{
struct sa_cma_buffer_info *info = dmabuf->priv;
return info->vaddr + offset * PAGE_SIZE;
+}
+static struct dma_buf_ops sa_dma_buf_ops = {
.map_dma_buf = sa_cma_map_dma_buf,
.unmap_dma_buf = sa_cma_unmap_dma_buf,
.mmap = sa_cma_mmap_dma_buf,
.release = sa_cma_release_dma_buf,
.kmap_atomic = sa_cma_kmap_dma_buf,
.kmap = sa_cma_kmap_dma_buf,
+};
+static struct dma_buf *sa_cma_allocate(struct sa_device *sadev,
u64 length, u32 flags)
+{
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
struct sa_cma_buffer_info *info;
struct dma_buf *dmabuf;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return NULL;
info->dev = sadev->dev.parent;
info->size = round_up(length, PAGE_SIZE);
info->vaddr = dma_alloc_wc(info->dev, info->size, &info->handle,
GFP_KERNEL | __GFP_NOWARN);
if (!info->vaddr)
goto cleanup;
exp_info.ops = &sa_dma_buf_ops;
exp_info.size = info->size;
exp_info.flags = flags;
exp_info.priv = info;
dmabuf = dma_buf_export(&exp_info);
if (IS_ERR(dmabuf))
goto export_failed;
return dmabuf;
+export_failed:
dma_free_wc(info->dev, info->size, info->vaddr, info->handle);
+cleanup:
kfree(info);
return NULL;
+}
+struct sa_device *simple_allocator_register_cma(struct device *dev) +{
struct sa_device *sadev;
int ret;
sadev = kzalloc(sizeof(*sadev), GFP_KERNEL);
if (!sadev)
return NULL;
sadev->dev.parent = dev;
sadev->owner = THIS_MODULE;
sadev->name = "cma";
sadev->allocate = sa_cma_allocate;
ret = simple_allocator_register(sadev);
if (ret) {
kfree(sadev);
return NULL;
}
return sadev;
+}
+static int __init sa_cma_init(void) +{
default_cma = simple_allocator_register_cma(NULL);
return 0;
+}
+static void __exit sa_cma_exit(void) +{
simple_allocator_unregister(default_cma);
+}
+module_init(sa_cma_init); +module_exit(sa_cma_exit);
I really don't like having to force the CMA allocation through the device structure. This ends up resulting in the need to create a dummy device just to allocate CMA memory. I'd rather see this code reworked to not require calling dma_alloc and just call cma_alloc directly. This memory can latter be mapped with dma_map.
cma_areas array is static in cma.c and I need it content to call cma_alloc. The problem is the same with cma_area_count.
I will have a look into cma.c to see if I can remove those constraints
This has happened in the powerpc kvm stuff, see arch/powerpc/kvm/book3s_hv_builtin.c The most difficult part is going to be figuring out how to hook up to do the early reservations.
I do not plan to reserve new cma areas with this driver but only provide an userland API to allocate buffer on already created ones. Since my module will be called long time after early reservation I think I will not have any issue.
You were right, cma_areas and cma_area_count are exported so I can use them in my driver. I have changed my code to use cma_alloc instead of dma_alloc. I will send a RFC with this content.
Benjamin
Thanks, Laura