From: CMY Date: Fri, 14 Mar 2014 09:41:32 +0000 (+0800) Subject: rk: ion: support cache ops for CMA heap X-Git-Tag: firefly_0821_release~6084 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=98ad00141cb4382996166f9cd82e9d203b256864;p=firefly-linux-kernel-4.4.55.git rk: ion: support cache ops for CMA heap --- diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h old mode 100644 new mode 100755 index 5b579b951503..939a00d1c64f --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -206,6 +206,31 @@ static inline void dma_free_writecombine(struct device *dev, size_t size, return dma_free_attrs(dev, size, cpu_addr, dma_handle, &attrs); } +static inline void *dma_alloc_nonconsistent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + DEFINE_DMA_ATTRS(attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); + return dma_alloc_attrs(dev, size, dma_handle, flag, &attrs); +} + +static inline void dma_free_nonconsistent(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle) +{ + DEFINE_DMA_ATTRS(attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); + return dma_free_attrs(dev, size, cpu_addr, dma_handle, &attrs); +} + +static inline int dma_mmap_nonconsistent(struct device *dev, + struct vm_area_struct *vma, void *cpu_addr, + dma_addr_t dma_addr, size_t size) +{ + DEFINE_DMA_ATTRS(attrs); + dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); + return dma_mmap_attrs(dev, vma, cpu_addr, dma_addr, size, &attrs); +} + /* * This can be called during early boot to increase the size of the atomic * coherent DMA pool above the default value of 256KiB. It must be called diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 2f49b3772086..f7c6441f6386 100755 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "ion.h" #include "ion_priv.h" @@ -1387,6 +1388,47 @@ static const struct file_operations ion_fops = { .compat_ioctl = compat_ion_ioctl, }; +int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle, + void *uaddr, unsigned long offset, unsigned long len, + unsigned int cmd) +{ + struct ion_buffer *buffer; + int ret = -EINVAL; + + mutex_lock(&client->lock); + if (!ion_handle_validate(client, handle)) { + pr_err("%s: invalid handle passed to do_cache_op.\n", + __func__); + mutex_unlock(&client->lock); + return -EINVAL; + } + buffer = handle->buffer; + mutex_lock(&buffer->lock); + + if (!ION_IS_CACHED(buffer->flags)) { + ret = 0; + goto out; + } + + if (!handle->buffer->heap->ops->cache_op) { + pr_err("%s: cache_op is not implemented by this heap.\n", + __func__); + ret = -ENODEV; + goto out; + } + + + ret = buffer->heap->ops->cache_op(buffer->heap, buffer, uaddr, + offset, len, cmd); + +out: + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); + return ret; + +} +EXPORT_SYMBOL(ion_do_cache_op); + static size_t ion_debug_heap_total(struct ion_client *client, unsigned int id) { diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c index 4418bda76474..bd606ca4eb80 100755 --- a/drivers/staging/android/ion/ion_cma_heap.c +++ b/drivers/staging/android/ion/ion_cma_heap.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "ion.h" #include "ion_priv.h" @@ -37,6 +39,7 @@ struct ion_cma_buffer_info { void *cpu_addr; dma_addr_t handle; struct sg_table *table; + bool is_cached; }; /* @@ -69,8 +72,8 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, dev_dbg(dev, "Request buffer allocation len %ld\n", len); - if (buffer->flags & ION_FLAG_CACHED) - return -EINVAL; +// if (buffer->flags & ION_FLAG_CACHED) +// return -EINVAL; if (align > PAGE_SIZE) return -EINVAL; @@ -81,8 +84,13 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, return ION_CMA_ALLOCATE_FAILED; } - info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle), - GFP_HIGHUSER | __GFP_ZERO); + if (!ION_IS_CACHED(flags)) { + info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle), + GFP_HIGHUSER | __GFP_ZERO); + } else { + info->cpu_addr = dma_alloc_nonconsistent(dev, len, + &(info->handle), GFP_HIGHUSER | __GFP_ZERO/*0*/); + } if (!info->cpu_addr) { dev_err(dev, "Fail to allocate buffer\n"); @@ -95,6 +103,8 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, goto free_mem; } + info->is_cached = ION_IS_CACHED(flags); + if (ion_cma_get_sgtable (dev, info->table, info->cpu_addr, info->handle, len)) goto free_table; @@ -165,8 +175,13 @@ static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer, struct device *dev = cma_heap->dev; struct ion_cma_buffer_info *info = buffer->priv_virt; - return dma_mmap_coherent(dev, vma, info->cpu_addr, info->handle, - buffer->size); + + if (info->is_cached) + return dma_mmap_nonconsistent(dev, vma, info->cpu_addr, + info->handle, buffer->size); + else + return dma_mmap_coherent(dev, vma, info->cpu_addr, info->handle, + buffer->size); } static void *ion_cma_map_kernel(struct ion_heap *heap, @@ -182,6 +197,36 @@ static void ion_cma_unmap_kernel(struct ion_heap *heap, { } +int ion_cma_cache_ops(struct ion_heap *heap, + struct ion_buffer *buffer, void *vaddr, + unsigned int offset, unsigned int length, + unsigned int cmd) +{ + struct ion_cma_buffer_info *info = buffer->priv_virt; + void (*outer_cache_op)(phys_addr_t, phys_addr_t); + + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + dmac_map_area(vaddr, length, DMA_TO_DEVICE); + outer_cache_op = outer_clean_range; + break; + case ION_IOC_INV_CACHES: + dmac_unmap_area(vaddr, length, DMA_FROM_DEVICE); + outer_cache_op = outer_inv_range; + break; + case ION_IOC_CLEAN_INV_CACHES: + dmac_flush_range(vaddr, vaddr + length); + outer_cache_op = outer_flush_range; + break; + default: + return -EINVAL; + } + + outer_cache_op(info->handle, info->handle + length); + + return 0; +} + static struct ion_heap_ops ion_cma_ops = { .allocate = ion_cma_allocate, .free = ion_cma_free, @@ -191,6 +236,7 @@ static struct ion_heap_ops ion_cma_ops = { .map_user = ion_cma_mmap, .map_kernel = ion_cma_map_kernel, .unmap_kernel = ion_cma_unmap_kernel, + .cache_op = ion_cma_cache_ops, }; struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data) diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h old mode 100644 new mode 100755 index 9bcd077f251c..696aca05ee27 --- a/drivers/staging/android/ion/ion_priv.h +++ b/drivers/staging/android/ion/ion_priv.h @@ -122,6 +122,9 @@ struct ion_heap_ops { int (*map_user) (struct ion_heap *mapper, struct ion_buffer *buffer, struct vm_area_struct *vma); int (*shrink)(struct ion_heap *heap, gfp_t gfp_mask, int nr_to_scan); + int (*cache_op)(struct ion_heap *heap, struct ion_buffer *buffer, + void *vaddr, unsigned int offset, + unsigned int length, unsigned int cmd); }; /** diff --git a/drivers/staging/android/ion/rockchip/rockchip_ion.c b/drivers/staging/android/ion/rockchip/rockchip_ion.c index 48c76eef0e87..5673ad315b97 100755 --- a/drivers/staging/android/ion/rockchip/rockchip_ion.c +++ b/drivers/staging/android/ion/rockchip/rockchip_ion.c @@ -44,6 +44,10 @@ extern struct ion_handle *ion_handle_get_by_id(struct ion_client *client, int id); extern int ion_handle_put(struct ion_handle *handle); +extern int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle, + void *uaddr, unsigned long offset, unsigned long len, + unsigned int cmd); + static struct ion_heap_desc ion_heap_meta[] = { { .id = ION_SYSTEM_HEAP_ID, @@ -219,6 +223,31 @@ struct ion_client *rockchip_ion_client_create(const char *name) } EXPORT_SYMBOL(rockchip_ion_client_create); +static int check_vaddr_bounds(unsigned long start, unsigned long end) +{ + struct mm_struct *mm = current->active_mm; + struct vm_area_struct *vma; + int ret = 1; + + if (end < start) + goto out; + + down_read(&mm->mmap_sem); + vma = find_vma(mm, start); + if (vma && vma->vm_start < end) { + if (start < vma->vm_start) + goto out_up; + if (end > vma->vm_end) + goto out_up; + ret = 0; + } + +out_up: + up_read(&mm->mmap_sem); +out: + return ret; +} + static long rockchip_custom_ioctl (struct ion_client *client, unsigned int cmd, unsigned long arg) { @@ -228,7 +257,50 @@ static long rockchip_custom_ioctl (struct ion_client *client, unsigned int cmd, case ION_IOC_CLEAN_CACHES: case ION_IOC_INV_CACHES: case ION_IOC_CLEAN_INV_CACHES: + { + struct ion_flush_data data; + unsigned long start, end; + struct ion_handle *handle = NULL; + int ret; + int is_import_fd = 0; + + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_flush_data))) + return -EFAULT; + + start = (unsigned long) data.vaddr; + end = (unsigned long) data.vaddr + data.length; + + if (check_vaddr_bounds(start, end)) { + pr_err("%s: virtual address %p is out of bounds\n", + __func__, data.vaddr); + return -EINVAL; + } + + handle = ion_handle_get_by_id(client, data.handle); + + if (IS_ERR(handle)) { + handle = ion_import_dma_buf(client, data.fd); + is_import_fd = 1; + if (IS_ERR(handle)) { + pr_info("%s: Could not import handle: %d\n", + __func__, (int)handle); + return -EINVAL; + } + } + + ret = ion_do_cache_op(client, handle, + data.vaddr, data.offset, data.length,cmd); + + if (is_import_fd) + ion_free(client, handle); + else + ion_handle_put(handle); + + if (ret < 0) + return ret; break; + } case ION_IOC_GET_PHYS: { struct ion_phys_data data; diff --git a/include/linux/rockchip_ion.h b/include/linux/rockchip_ion.h old mode 100644 new mode 100755