Current ion kernel mapping implementation uses vmap() to map previously allocated buffers into kernel virtual address space. On 32-bit platforms, vmap() might fail on large enough buffers due to the limited available vmalloc space. dma_buf_kmap() should guarantee that only one page is mapped at a time. To fix this, kmap()/kmap_atomic() is used to implement the appropriate interfaces. Signed-off-by: Alexey Skidanov <alexey.skidanov@xxxxxxxxx> --- drivers/staging/android/ion/ion.c | 97 +++++++++++++++++++-------------------- drivers/staging/android/ion/ion.h | 1 - 2 files changed, 48 insertions(+), 50 deletions(-) diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 57e0d80..75830e3 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -27,6 +27,7 @@ #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/vmalloc.h> +#include <linux/highmem.h> #include "ion.h" @@ -119,8 +120,6 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, void ion_buffer_destroy(struct ion_buffer *buffer) { - if (WARN_ON(buffer->kmap_cnt > 0)) - buffer->heap->ops->unmap_kernel(buffer->heap, buffer); buffer->heap->ops->free(buffer); kfree(buffer); } @@ -140,34 +139,6 @@ static void _ion_buffer_destroy(struct ion_buffer *buffer) ion_buffer_destroy(buffer); } -static void *ion_buffer_kmap_get(struct ion_buffer *buffer) -{ - void *vaddr; - - if (buffer->kmap_cnt) { - buffer->kmap_cnt++; - return buffer->vaddr; - } - vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer); - if (WARN_ONCE(!vaddr, - "heap->ops->map_kernel should return ERR_PTR on error")) - return ERR_PTR(-EINVAL); - if (IS_ERR(vaddr)) - return vaddr; - buffer->vaddr = vaddr; - buffer->kmap_cnt++; - return vaddr; -} - -static void ion_buffer_kmap_put(struct ion_buffer *buffer) -{ - buffer->kmap_cnt--; - if (!buffer->kmap_cnt) { - buffer->heap->ops->unmap_kernel(buffer->heap, buffer); - buffer->vaddr = NULL; - } -} - static struct sg_table *dup_sg_table(struct sg_table *table) { struct sg_table *new_table; @@ -305,34 +276,68 @@ static void ion_dma_buf_release(struct dma_buf *dmabuf) _ion_buffer_destroy(buffer); } +static inline int sg_page_count(struct scatterlist *sg) +{ + return PAGE_ALIGN(sg->offset + sg->length) >> PAGE_SHIFT; +} + +static struct page *ion_dma_buf_get_page(struct sg_table *sg_table, + unsigned long offset) +{ + struct page *page; + struct scatterlist *sg; + int i; + unsigned int nr_pages; + + nr_pages = 0; + page = NULL; + /* Find the page with specified offset */ + for_each_sg(sg_table->sgl, sg, sg_table->nents, i) { + if (nr_pages + sg_page_count(sg) > offset) { + page = nth_page(sg_page(sg), offset - nr_pages); + break; + } + + nr_pages += sg_page_count(sg); + } + + return page; +} static void *ion_dma_buf_kmap(struct dma_buf *dmabuf, unsigned long offset) { struct ion_buffer *buffer = dmabuf->priv; - return buffer->vaddr + offset * PAGE_SIZE; + return kmap(ion_dma_buf_get_page(buffer->sg_table, offset)); } static void ion_dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long offset, void *ptr) { + kunmap(ptr); +} + +static void *ion_dma_buf_kmap_atomic(struct dma_buf *dmabuf, + unsigned long offset) +{ + struct ion_buffer *buffer = dmabuf->priv; + + return kmap_atomic(ion_dma_buf_get_page(buffer->sg_table, + offset)); +} + +static void ion_dma_buf_kunmap_atomic(struct dma_buf *dmabuf, + unsigned long offset, + void *ptr) +{ + kunmap_atomic(ptr); } static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction direction) { struct ion_buffer *buffer = dmabuf->priv; - void *vaddr; struct ion_dma_buf_attachment *a; - /* - * TODO: Move this elsewhere because we don't always need a vaddr - */ - if (buffer->heap->ops->map_kernel) { - mutex_lock(&buffer->lock); - vaddr = ion_buffer_kmap_get(buffer); - mutex_unlock(&buffer->lock); - } - mutex_lock(&buffer->lock); list_for_each_entry(a, &buffer->attachments, list) { dma_sync_sg_for_cpu(a->dev, a->table->sgl, a->table->nents, @@ -349,12 +354,6 @@ static int ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, struct ion_buffer *buffer = dmabuf->priv; struct ion_dma_buf_attachment *a; - if (buffer->heap->ops->map_kernel) { - mutex_lock(&buffer->lock); - ion_buffer_kmap_put(buffer); - mutex_unlock(&buffer->lock); - } - mutex_lock(&buffer->lock); list_for_each_entry(a, &buffer->attachments, list) { dma_sync_sg_for_device(a->dev, a->table->sgl, a->table->nents, @@ -374,8 +373,8 @@ static const struct dma_buf_ops dma_buf_ops = { .detach = ion_dma_buf_detatch, .begin_cpu_access = ion_dma_buf_begin_cpu_access, .end_cpu_access = ion_dma_buf_end_cpu_access, - .map_atomic = ion_dma_buf_kmap, - .unmap_atomic = ion_dma_buf_kunmap, + .map_atomic = ion_dma_buf_kmap_atomic, + .unmap_atomic = ion_dma_buf_kunmap_atomic, .map = ion_dma_buf_kmap, .unmap = ion_dma_buf_kunmap, }; diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h index a238f23..e2bbd74 100644 --- a/drivers/staging/android/ion/ion.h +++ b/drivers/staging/android/ion/ion.h @@ -72,7 +72,6 @@ struct ion_buffer { size_t size; void *priv_virt; struct mutex lock; - int kmap_cnt; void *vaddr; struct sg_table *sg_table; struct list_head attachments; -- 2.7.4 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel