From c4715194082e79c7fb8e82e0f373f27a01ed3676 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 3 Jul 2013 15:48:04 -0700 Subject: [PATCH 01/49] ARM: kgdb: ignore breakpoint instructions from user mode Avoid conflicts with user mode usage of the same instructions, as with Clang -ftrapv. Change-Id: I12d1c6d8f94376bfd2503cb0be843d7e478fb6ea Signed-off-by: Todd Poynor --- arch/arm/kernel/kgdb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c index 778c2f7024ff..b321c8fbb87d 100644 --- a/arch/arm/kernel/kgdb.c +++ b/arch/arm/kernel/kgdb.c @@ -144,6 +144,8 @@ int kgdb_arch_handle_exception(int exception_vector, int signo, static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr) { + if (user_mode(regs)) + return -1; kgdb_handle_exception(1, SIGTRAP, 0, regs); return 0; @@ -151,6 +153,8 @@ static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr) static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr) { + if (user_mode(regs)) + return -1; compiled_break = 1; kgdb_handle_exception(1, SIGTRAP, 0, regs); From 0a87e75ab5ab0d8a14a3f293d4970f9b3e50bf6e Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Fri, 22 Mar 2013 18:08:29 +0100 Subject: [PATCH 02/49] gpu: ion: fix ion_platform_data definition fix ion_platform_heap to make is use an usual way in board configuration file. Signed-off-by: Benjamin Gaignard --- include/linux/ion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/ion.h b/include/linux/ion.h index 8414a6d9c39b..2ce49d05fa63 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -105,7 +105,7 @@ struct ion_platform_heap { */ struct ion_platform_data { int nr; - struct ion_platform_heap heaps[]; + struct ion_platform_heap *heaps; }; /** From ae2b22e9b7a207d9392df5b5ec93b8c042d4b3ce Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Fri, 22 Mar 2013 18:08:30 +0100 Subject: [PATCH 03/49] gpu: ion: add CMA heap New heap type ION_HEAP_TYPE_DMA where allocation is done with dma_alloc_coherent API. device coherent_dma_mask must be set to DMA_BIT_MASK(32). ion_platform_heap private field is used to retrieve the device linked to CMA, if NULL the default CMA area is used. ion_cma_get_sgtable is a copy of dma_common_get_sgtable function which should be in kernel 3.5 Change-Id: If4b1a3f9c8a6bd72053226208832f4971e44372f Signed-off-by: Benjamin Gaignard --- drivers/gpu/ion/Makefile | 2 +- drivers/gpu/ion/ion_cma_heap.c | 234 +++++++++++++++++++++++++++++++++ drivers/gpu/ion/ion_heap.c | 6 + drivers/gpu/ion/ion_priv.h | 3 + include/linux/ion.h | 3 + 5 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/ion/ion_cma_heap.c diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile index 306fff970de4..ce0f1ef1c233 100644 --- a/drivers/gpu/ion/Makefile +++ b/drivers/gpu/ion/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_ION) += ion.o ion_heap.o ion_page_pool.o ion_system_heap.o \ - ion_carveout_heap.o ion_chunk_heap.o + ion_carveout_heap.o ion_chunk_heap.o ion_cma_heap.o obj-$(CONFIG_ION_TEGRA) += tegra/ diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c new file mode 100644 index 000000000000..a3e0b5475bc6 --- /dev/null +++ b/drivers/gpu/ion/ion_cma_heap.c @@ -0,0 +1,234 @@ +/* + * drivers/gpu/ion/ion_cma_heap.c + * + * Copyright (C) Linaro 2012 + * Author: for ST-Ericsson. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +/* for ion_heap_ops structure */ +#include "ion_priv.h" + +#define ION_CMA_ALLOCATE_FAILED -1 + +struct ion_cma_heap { + struct ion_heap heap; + struct device *dev; +}; + +#define to_cma_heap(x) container_of(x, struct ion_cma_heap, heap) + +struct ion_cma_buffer_info { + void *cpu_addr; + dma_addr_t handle; + struct sg_table *table; +}; + +/* + * Create scatter-list for the already allocated DMA buffer. + * This function could be replaced by dma_common_get_sgtable + * as soon as it will avalaible. + */ +int ion_cma_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t handle, size_t size) +{ + struct page *page = virt_to_page(cpu_addr); + int ret; + + ret = sg_alloc_table(sgt, 1, GFP_KERNEL); + if (unlikely(ret)) + return ret; + + sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0); + return 0; +} + +/* + * Create scatter-list for each page of the already allocated DMA buffer. + */ +int ion_cma_get_sgtable_per_page(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t handle, size_t size) +{ + struct page *page = virt_to_page(cpu_addr); + int ret, i; + struct scatterlist *sg; + + ret = sg_alloc_table(sgt, PAGE_ALIGN(size) / PAGE_SIZE, GFP_KERNEL); + if (unlikely(ret)) + return ret; + + sg = sgt->sgl; + for (i = 0; i < (PAGE_ALIGN(size) / PAGE_SIZE); i++) { + page = virt_to_page(cpu_addr + (i * PAGE_SIZE)); + sg_set_page(sg, page, PAGE_SIZE, 0); + sg = sg_next(sg); + } + return 0; +} + +/* ION CMA heap operations functions */ +static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, + unsigned long len, unsigned long align, + unsigned long flags) +{ + struct ion_cma_heap *cma_heap = to_cma_heap(heap); + struct device *dev = cma_heap->dev; + struct ion_cma_buffer_info *info; + + dev_dbg(dev, "Request buffer allocation len %ld\n", len); + + info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL); + if (!info) { + dev_err(dev, "Can't allocate buffer info\n"); + return ION_CMA_ALLOCATE_FAILED; + } + + info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle), 0); + + if (!info->cpu_addr) { + dev_err(dev, "Fail to allocate buffer\n"); + goto err; + } + + info->table = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!info->table) { + dev_err(dev, "Fail to allocate sg table\n"); + goto free_mem; + } + + if (ion_buffer_fault_user_mappings(buffer)) { + if (ion_cma_get_sgtable_per_page + (dev, info->table, info->cpu_addr, info->handle, len)) + goto free_table; + } else { + if (ion_cma_get_sgtable + (dev, info->table, info->cpu_addr, info->handle, len)) + goto free_table; + } + /* keep this for memory release */ + buffer->priv_virt = info; + dev_dbg(dev, "Allocate buffer %p\n", buffer); + return 0; + +free_table: + kfree(info->table); +free_mem: + dma_free_coherent(dev, len, info->cpu_addr, info->handle); +err: + kfree(info); + return ION_CMA_ALLOCATE_FAILED; +} + +static void ion_cma_free(struct ion_buffer *buffer) +{ + struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap); + struct device *dev = cma_heap->dev; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + dev_dbg(dev, "Release buffer %p\n", buffer); + /* release memory */ + dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle); + /* release sg table */ + sg_free_table(info->table); + kfree(info->table); + kfree(info); +} + +/* return physical address in addr */ +static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer, + ion_phys_addr_t *addr, size_t *len) +{ + struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap); + struct device *dev = cma_heap->dev; + struct ion_cma_buffer_info *info = buffer->priv_virt; + + dev_dbg(dev, "Return buffer %p physical address 0x%x\n", buffer, + info->handle); + + *addr = info->handle; + *len = buffer->size; + + return 0; +} + +struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct ion_cma_buffer_info *info = buffer->priv_virt; + + return info->table; +} + +void ion_cma_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + return; +} + +static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer, + struct vm_area_struct *vma) +{ + struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap); + 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); +} + +void *ion_cma_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer) +{ + struct ion_cma_buffer_info *info = buffer->priv_virt; + /* kernel memory mapping has been done at allocation time */ + return info->cpu_addr; +} + +static struct ion_heap_ops ion_cma_ops = { + .allocate = ion_cma_allocate, + .free = ion_cma_free, + .map_dma = ion_cma_heap_map_dma, + .unmap_dma = ion_cma_heap_unmap_dma, + .phys = ion_cma_phys, + .map_user = ion_cma_mmap, + .map_kernel = ion_cma_map_kernel, +}; + +struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data) +{ + struct ion_cma_heap *cma_heap; + + cma_heap = kzalloc(sizeof(struct ion_cma_heap), GFP_KERNEL); + + if (!cma_heap) + return ERR_PTR(-ENOMEM); + + cma_heap->heap.ops = &ion_cma_ops; + /* get device from private heaps data, later it will be + * used to make the link with reserved CMA memory */ + cma_heap->dev = data->priv; + cma_heap->heap.type = ION_HEAP_TYPE_DMA; + return &cma_heap->heap; +} + +void ion_cma_heap_destroy(struct ion_heap *heap) +{ + struct ion_cma_heap *cma_heap = to_cma_heap(heap); + + kfree(cma_heap); +} diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c index 4a16aa27384c..05e7ce5499c4 100644 --- a/drivers/gpu/ion/ion_heap.c +++ b/drivers/gpu/ion/ion_heap.c @@ -254,6 +254,9 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) case ION_HEAP_TYPE_CHUNK: heap = ion_chunk_heap_create(heap_data); break; + case ION_HEAP_TYPE_DMA: + heap = ion_cma_heap_create(heap_data); + break; default: pr_err("%s: Invalid heap type %d\n", __func__, heap_data->type); @@ -290,6 +293,9 @@ void ion_heap_destroy(struct ion_heap *heap) case ION_HEAP_TYPE_CHUNK: ion_chunk_heap_destroy(heap); break; + case ION_HEAP_TYPE_DMA: + ion_cma_heap_destroy(heap); + break; default: pr_err("%s: Invalid heap type %d\n", __func__, heap->type); diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index f9bccbc131ed..44db865d1e1c 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -269,6 +269,9 @@ void ion_carveout_heap_destroy(struct ion_heap *); struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *); void ion_chunk_heap_destroy(struct ion_heap *); +struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *); +void ion_cma_heap_destroy(struct ion_heap *); + /** * kernel api to allocate/free from carveout -- used when carveout is * used to back an architecture specific custom heap diff --git a/include/linux/ion.h b/include/linux/ion.h index 2ce49d05fa63..440f4b3b0a4a 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -27,6 +27,7 @@ struct ion_handle; * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved * carveout heap, allocations are physically * contiguous + * @ION_HEAP_TYPE_DMA: memory allocated via DMA API * @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask * is used to identify the heaps, so only 32 * total heap types are supported @@ -36,6 +37,7 @@ enum ion_heap_type { ION_HEAP_TYPE_SYSTEM_CONTIG, ION_HEAP_TYPE_CARVEOUT, ION_HEAP_TYPE_CHUNK, + ION_HEAP_TYPE_DMA, ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always are at the end of this enum */ ION_NUM_HEAPS = 16, @@ -44,6 +46,7 @@ enum ion_heap_type { #define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM) #define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG) #define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT) +#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA) #define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8 From bbf5538ee0e4c30ba9b4556f9699ed5f31df67da Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Zavin Date: Wed, 12 Jun 2013 15:22:16 -0700 Subject: [PATCH 04/49] gpu: ion: Fix performance issue in faulting code Previously the code to fault ion buffers in one page at a time had a performance problem caused by the requirement to traverse the sg list looking for the right page to load in (a result of the fact that the items in the list may not be of uniform size). To fix the problem, for buffers that will be faulted in, also keep a flat array of all the pages in the buffer to use from the fault handler. To recover some of the additional memory footprint this creates per buffer, dirty bits used to indicate which pages have been faulted in to the cpu are now stored in the low bit of each page struct pointer in the page array. Change-Id: I891b077dc0c88ed6d416b256626d8778fd67be84 Signed-off-by: Rebecca Schultz Zavin --- drivers/gpu/ion/ion.c | 105 +++++++++++++++++------------- drivers/gpu/ion/ion_cma_heap.c | 35 +--------- drivers/gpu/ion/ion_heap.c | 18 ++++- drivers/gpu/ion/ion_priv.h | 20 ++++-- drivers/gpu/ion/ion_system_heap.c | 25 ++----- 5 files changed, 99 insertions(+), 104 deletions(-) diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index 6c93365c3dbc..dbe5bbce6a54 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -104,13 +105,33 @@ struct ion_handle { bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer) { - return ((buffer->flags & ION_FLAG_CACHED) && - !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC)); + return ((buffer->flags & ION_FLAG_CACHED) && + !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC)); } bool ion_buffer_cached(struct ion_buffer *buffer) { - return !!(buffer->flags & ION_FLAG_CACHED); + return !!(buffer->flags & ION_FLAG_CACHED); +} + +static inline struct page *ion_buffer_page(struct page *page) +{ + return (struct page *)((unsigned long)page & ~(1UL)); +} + +static inline bool ion_buffer_page_is_dirty(struct page *page) +{ + return !!((unsigned long)page & 1UL); +} + +static inline void ion_buffer_page_dirty(struct page **page) +{ + *page = (struct page *)((unsigned long)(*page) | 1UL); +} + +static inline void ion_buffer_page_clean(struct page **page) +{ + *page = (struct page *)((unsigned long)(*page) & ~(1UL)); } /* this function should only be called while dev->lock is held */ @@ -139,8 +160,6 @@ static void ion_buffer_add(struct ion_device *dev, rb_insert_color(&buffer->node, &dev->buffers); } -static int ion_buffer_alloc_dirty(struct ion_buffer *buffer); - /* this function should only be called while dev->lock is held */ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, struct ion_device *dev, @@ -185,17 +204,23 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, } buffer->sg_table = table; if (ion_buffer_fault_user_mappings(buffer)) { - for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, - i) { - if (sg_dma_len(sg) == PAGE_SIZE) - continue; - pr_err("%s: cached mappings that will be faulted in " - "must have pagewise sg_lists\n", __func__); - ret = -EINVAL; - goto err; + int num_pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; + struct scatterlist *sg; + int i, j, k = 0; + + buffer->pages = vmalloc(sizeof(struct page *) * num_pages); + if (!buffer->pages) { + ret = -ENOMEM; + goto err1; + } + + for_each_sg(table->sgl, sg, table->nents, i) { + struct page *page = sg_page(sg); + + for (j = 0; j < sg_dma_len(sg) / PAGE_SIZE; j++) + buffer->pages[k++] = page++; } - ret = ion_buffer_alloc_dirty(buffer); if (ret) goto err; } @@ -222,6 +247,9 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, err: heap->ops->unmap_dma(heap, buffer); heap->ops->free(buffer); +err1: + if (buffer->pages) + vfree(buffer->pages); err2: kfree(buffer); return ERR_PTR(ret); @@ -233,8 +261,8 @@ void ion_buffer_destroy(struct ion_buffer *buffer) buffer->heap->ops->unmap_kernel(buffer->heap, buffer); buffer->heap->ops->unmap_dma(buffer->heap, buffer); buffer->heap->ops->free(buffer); - if (buffer->flags & ION_FLAG_CACHED) - kfree(buffer->dirty); + if (buffer->pages) + vfree(buffer->pages); kfree(buffer); } @@ -764,17 +792,6 @@ static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment, { } -static int ion_buffer_alloc_dirty(struct ion_buffer *buffer) -{ - unsigned long pages = buffer->sg_table->nents; - unsigned long length = (pages + BITS_PER_LONG - 1)/BITS_PER_LONG; - - buffer->dirty = kzalloc(length * sizeof(unsigned long), GFP_KERNEL); - if (!buffer->dirty) - return -ENOMEM; - return 0; -} - struct ion_vma_list { struct list_head list; struct vm_area_struct *vma; @@ -784,9 +801,9 @@ static void ion_buffer_sync_for_device(struct ion_buffer *buffer, struct device *dev, enum dma_data_direction dir) { - struct scatterlist *sg; - int i; struct ion_vma_list *vma_list; + int pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; + int i; pr_debug("%s: syncing for device %s\n", __func__, dev ? dev_name(dev) : "null"); @@ -795,11 +812,12 @@ static void ion_buffer_sync_for_device(struct ion_buffer *buffer, return; mutex_lock(&buffer->lock); - for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { - if (!test_bit(i, buffer->dirty)) - continue; - dma_sync_sg_for_device(dev, sg, 1, dir); - clear_bit(i, buffer->dirty); + for (i = 0; i < pages; i++) { + struct page *page = buffer->pages[i]; + + if (ion_buffer_page_is_dirty(page)) + __dma_page_cpu_to_dev(page, 0, PAGE_SIZE, dir); + ion_buffer_page_clean(buffer->pages + i); } list_for_each_entry(vma_list, &buffer->vmas, list) { struct vm_area_struct *vma = vma_list->vma; @@ -813,21 +831,18 @@ static void ion_buffer_sync_for_device(struct ion_buffer *buffer, int ion_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct ion_buffer *buffer = vma->vm_private_data; - struct scatterlist *sg; - int i; + int ret; mutex_lock(&buffer->lock); - set_bit(vmf->pgoff, buffer->dirty); + ion_buffer_page_dirty(buffer->pages + vmf->pgoff); - for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { - if (i != vmf->pgoff) - continue; - dma_sync_sg_for_cpu(NULL, sg, 1, DMA_BIDIRECTIONAL); - vm_insert_page(vma, (unsigned long)vmf->virtual_address, - sg_page(sg)); - break; - } + BUG_ON(!buffer->pages || !buffer->pages[vmf->pgoff]); + ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, + ion_buffer_page(buffer->pages[vmf->pgoff])); mutex_unlock(&buffer->lock); + if (ret) + return VM_FAULT_ERROR; + return VM_FAULT_NOPAGE; } diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c index a3e0b5475bc6..1eaa8c11e04c 100644 --- a/drivers/gpu/ion/ion_cma_heap.c +++ b/drivers/gpu/ion/ion_cma_heap.c @@ -59,29 +59,6 @@ int ion_cma_get_sgtable(struct device *dev, struct sg_table *sgt, return 0; } -/* - * Create scatter-list for each page of the already allocated DMA buffer. - */ -int ion_cma_get_sgtable_per_page(struct device *dev, struct sg_table *sgt, - void *cpu_addr, dma_addr_t handle, size_t size) -{ - struct page *page = virt_to_page(cpu_addr); - int ret, i; - struct scatterlist *sg; - - ret = sg_alloc_table(sgt, PAGE_ALIGN(size) / PAGE_SIZE, GFP_KERNEL); - if (unlikely(ret)) - return ret; - - sg = sgt->sgl; - for (i = 0; i < (PAGE_ALIGN(size) / PAGE_SIZE); i++) { - page = virt_to_page(cpu_addr + (i * PAGE_SIZE)); - sg_set_page(sg, page, PAGE_SIZE, 0); - sg = sg_next(sg); - } - return 0; -} - /* ION CMA heap operations functions */ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, unsigned long len, unsigned long align, @@ -112,15 +89,9 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, goto free_mem; } - if (ion_buffer_fault_user_mappings(buffer)) { - if (ion_cma_get_sgtable_per_page - (dev, info->table, info->cpu_addr, info->handle, len)) - goto free_table; - } else { - if (ion_cma_get_sgtable - (dev, info->table, info->cpu_addr, info->handle, len)) - goto free_table; - } + if (ion_cma_get_sgtable + (dev, info->table, info->cpu_addr, info->handle, len)) + goto free_table; /* keep this for memory release */ buffer->priv_virt = info; dev_dbg(dev, "Allocate buffer %p\n", buffer); diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c index 05e7ce5499c4..bf6a383bd98b 100644 --- a/drivers/gpu/ion/ion_heap.c +++ b/drivers/gpu/ion/ion_heap.c @@ -134,8 +134,22 @@ end: return ret; } -void ion_heap_free_page(struct ion_buffer *buffer, struct page *page, - unsigned int order) +struct page *ion_heap_alloc_pages(struct ion_buffer *buffer, gfp_t gfp_flags, + unsigned int order) +{ + struct page *page = alloc_pages(gfp_flags, order); + + if (!page) + return page; + + if (ion_buffer_fault_user_mappings(buffer)) + split_page(page, order); + + return page; +} + +void ion_heap_free_pages(struct ion_buffer *buffer, struct page *page, + unsigned int order) { int i; diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index 44db865d1e1c..1155994fc8ec 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -45,9 +45,8 @@ struct ion_buffer *ion_handle_buffer(struct ion_handle *handle); * @vaddr: the kenrel mapping if kmap_cnt is not zero * @dmap_cnt: number of times the buffer is mapped for dma * @sg_table: the sg table for the buffer if dmap_cnt is not zero - * @dirty: bitmask representing which pages of this buffer have - * been dirtied by the cpu and need cache maintenance - * before dma + * @pages: flat array of pages in the buffer -- used by fault + * handler and only valid for buffers that are faulted in * @vmas: list of vma's mapping this buffer * @handle_count: count of handles referencing this buffer * @task_comm: taskcomm of last client to reference this buffer in a @@ -74,7 +73,7 @@ struct ion_buffer { void *vaddr; int dmap_cnt; struct sg_table *sg_table; - unsigned long *dirty; + struct page **pages; struct list_head vmas; /* used to track orphaned buffers */ int handle_count; @@ -212,6 +211,19 @@ int ion_heap_map_user(struct ion_heap *, struct ion_buffer *, struct vm_area_struct *); int ion_heap_buffer_zero(struct ion_buffer *buffer); +/** + * ion_heap_alloc_pages - allocate pages from alloc_pages + * @buffer: the buffer to allocate for, used to extract the flags + * @gfp_flags: the gfp_t for the allocation + * @order: the order of the allocatoin + * + * This funciton allocations from alloc pages and also does any other + * necessary operations based on the buffer->flags. For buffers which + * will be faulted in the pages are split using split_page + */ +struct page *ion_heap_alloc_pages(struct ion_buffer *buffer, gfp_t gfp_flags, + unsigned int order); + /** * ion_heap_init_deferred_free -- initialize deferred free functionality * @heap: the heap diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c index e101db5da5b4..5fe81a76f2f5 100644 --- a/drivers/gpu/ion/ion_system_heap.c +++ b/drivers/gpu/ion/ion_system_heap.c @@ -64,7 +64,6 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap, unsigned long order) { bool cached = ion_buffer_cached(buffer); - bool split_pages = ion_buffer_fault_user_mappings(buffer); struct ion_page_pool *pool = heap->pools[order_to_index(order)]; struct page *page; @@ -75,7 +74,7 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap, if (order > 4) gfp_flags = high_order_gfp_flags; - page = alloc_pages(gfp_flags, order); + page = ion_heap_alloc_pages(buffer, gfp_flags, order); if (!page) return 0; arm_dma_ops.sync_single_for_device(NULL, @@ -85,8 +84,6 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap, if (!page) return 0; - if (split_pages) - split_page(page, order); return page; } @@ -153,7 +150,6 @@ static int ion_system_heap_allocate(struct ion_heap *heap, int i = 0; long size_remaining = PAGE_ALIGN(size); unsigned int max_order = orders[0]; - bool split_pages = ion_buffer_fault_user_mappings(buffer); INIT_LIST_HEAD(&pages); while (size_remaining > 0) { @@ -170,28 +166,15 @@ static int ion_system_heap_allocate(struct ion_heap *heap, if (!table) goto err; - if (split_pages) - ret = sg_alloc_table(table, PAGE_ALIGN(size) / PAGE_SIZE, - GFP_KERNEL); - else - ret = sg_alloc_table(table, i, GFP_KERNEL); - + ret = sg_alloc_table(table, i, GFP_KERNEL); if (ret) goto err1; sg = table->sgl; list_for_each_entry_safe(info, tmp_info, &pages, list) { struct page *page = info->page; - if (split_pages) { - for (i = 0; i < (1 << info->order); i++) { - sg_set_page(sg, page + i, PAGE_SIZE, 0); - sg = sg_next(sg); - } - } else { - sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE, - 0); - sg = sg_next(sg); - } + sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE, 0); + sg = sg_next(sg); list_del(&info->list); kfree(info); } From 2f42fa9141974d917c5a85fc484e48f53cf7ec71 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Thu, 1 Sep 2011 15:26:50 -0400 Subject: [PATCH 05/49] add extra free kbytes tunable Add a userspace visible knob to tell the VM to keep an extra amount of memory free, by increasing the gap between each zone's min and low watermarks. This is useful for realtime applications that call system calls and have a bound on the number of allocations that happen in any short time period. In this application, extra_free_kbytes would be left at an amount equal to or larger than than the maximum number of allocations that happen in any burst. It may also be useful to reduce the memory use of virtual machines (temporarily?), in a way that does not cause memory fragmentation like ballooning does. [ccross] Revived for use on old kernels where no other solution exists. The tunable will be removed on kernels that do better at avoiding direct reclaim. Change-Id: I765a42be8e964bfd3e2886d1ca85a29d60c3bb3e Signed-off-by: Rik van Riel Signed-off-by: Colin Cross --- Documentation/sysctl/vm.txt | 16 ++++++++++++++++ kernel/sysctl.c | 9 +++++++++ mm/page_alloc.c | 32 +++++++++++++++++++++++++------- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index dcc75a9ed919..b81fca90f7fe 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -29,6 +29,7 @@ Currently, these files are in /proc/sys/vm: - dirty_writeback_centisecs - drop_caches - extfrag_threshold +- extra_free_kbytes - hugepages_treat_as_movable - hugetlb_shm_group - laptop_mode @@ -198,6 +199,21 @@ fragmentation index is <= extfrag_threshold. The default value is 500. ============================================================== +extra_free_kbytes + +This parameter tells the VM to keep extra free memory between the threshold +where background reclaim (kswapd) kicks in, and the threshold where direct +reclaim (by allocating processes) kicks in. + +This is useful for workloads that require low latency memory allocations +and have a bounded burstiness in memory allocations, for example a +realtime application that receives and transmits network traffic +(causing in-kernel memory allocations) with a maximum total message burst +size of 200MB may need 200MB of extra free memory to avoid direct reclaim +related latencies. + +============================================================== + hugepages_treat_as_movable This parameter is only useful when kernelcore= is specified at boot time to diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 1a07798c9146..6f562baf5f4a 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -105,6 +105,7 @@ extern char core_pattern[]; extern unsigned int core_pipe_limit; #endif extern int pid_max; +extern int extra_free_kbytes; extern int min_free_order_shift; extern int pid_max_min, pid_max_max; extern int percpu_pagelist_fraction; @@ -1266,6 +1267,14 @@ static struct ctl_table vm_table[] = { .proc_handler = min_free_kbytes_sysctl_handler, .extra1 = &zero, }, + { + .procname = "extra_free_kbytes", + .data = &extra_free_kbytes, + .maxlen = sizeof(extra_free_kbytes), + .mode = 0644, + .proc_handler = min_free_kbytes_sysctl_handler, + .extra1 = &zero, + }, { .procname = "min_free_order_shift", .data = &min_free_order_shift, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 58f55f605ad3..336609267a04 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -196,9 +196,21 @@ static char * const zone_names[MAX_NR_ZONES] = { "Movable", }; +/* + * Try to keep at least this much lowmem free. Do not allow normal + * allocations below this point, only high priority ones. Automatically + * tuned according to the amount of memory in the system. + */ int min_free_kbytes = 1024; int min_free_order_shift = 1; +/* + * Extra memory for the system to try freeing. Used to temporarily + * free memory, to make space for new workloads. Anyone can allocate + * down to the min watermarks controlled by min_free_kbytes above. + */ +int extra_free_kbytes = 0; + static unsigned long __meminitdata nr_kernel_pages; static unsigned long __meminitdata nr_all_pages; static unsigned long __meminitdata dma_reserve; @@ -5321,6 +5333,7 @@ static void setup_per_zone_lowmem_reserve(void) static void __setup_per_zone_wmarks(void) { unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10); + unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10); unsigned long lowmem_pages = 0; struct zone *zone; unsigned long flags; @@ -5332,11 +5345,14 @@ static void __setup_per_zone_wmarks(void) } for_each_zone(zone) { - u64 tmp; + u64 min, low; spin_lock_irqsave(&zone->lock, flags); - tmp = (u64)pages_min * zone->managed_pages; - do_div(tmp, lowmem_pages); + min = (u64)pages_min * zone->managed_pages; + do_div(min, lowmem_pages); + low = (u64)pages_low * zone->managed_pages; + do_div(low, vm_total_pages); + if (is_highmem(zone)) { /* * __GFP_HIGH and PF_MEMALLOC allocations usually don't @@ -5357,11 +5373,13 @@ static void __setup_per_zone_wmarks(void) * If it's a lowmem zone, reserve a number of pages * proportionate to the zone's size. */ - zone->watermark[WMARK_MIN] = tmp; + zone->watermark[WMARK_MIN] = min; } - zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + (tmp >> 2); - zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> 1); + zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + + low + (min >> 2); + zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + + low + (min >> 1); setup_zone_migrate_reserve(zone); spin_unlock_irqrestore(&zone->lock, flags); @@ -5474,7 +5492,7 @@ module_init(init_per_zone_wmark_min) /* * min_free_kbytes_sysctl_handler - just a wrapper around proc_dointvec() so * that we can call two helper functions whenever min_free_kbytes - * changes. + * or extra_free_kbytes changes. */ int min_free_kbytes_sysctl_handler(ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) From b396b36a15b27767215ea6a35174a6dba0302ba1 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 30 Jul 2013 16:45:24 -0700 Subject: [PATCH 06/49] power_supply: kill android-battery driver Discontinued in favor of future userspace charging helpers. Change-Id: I840a94ff42e2219cfd8759f919f6188355a63d92 Signed-off-by: Todd Poynor --- android/configs/android-recommended.cfg | 1 - drivers/power/Kconfig | 10 - drivers/power/Makefile | 1 - drivers/power/android_battery.c | 692 ------------------ include/linux/platform_data/android_battery.h | 47 -- 5 files changed, 751 deletions(-) delete mode 100644 drivers/power/android_battery.c delete mode 100644 include/linux/platform_data/android_battery.h diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index ca3cc8613366..546c37f552bc 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -11,7 +11,6 @@ CONFIG_COMPACTION=y CONFIG_ANDROID_RAM_CONSOLE=y CONFIG_ANDROID_TIMED_GPIO=y CONFIG_BACKLIGHT_LCD_SUPPORT=y -CONFIG_BATTERY_ANDROID=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 430ef8c8b6a8..7b8979c63f48 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -204,16 +204,6 @@ config BATTERY_MAX17042 with MAX17042. This driver also supports max17047/50 chips which are improved version of max17042. -config BATTERY_ANDROID - tristate "Battery driver for Android" - help - Say Y to enable generic support for battery charging according - to common Android policies. - This driver adds periodic battery level and health monitoring, - kernel log reporting and other debugging features, common board - battery file glue logic for battery/case temperature sensors, - etc. - config BATTERY_Z2 tristate "Z2 battery driver" depends on I2C && MACH_ZIPIT2 diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 27d9deef51b6..653bf6ceff30 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_WM8350_POWER) += wm8350_power.o obj-$(CONFIG_TEST_POWER) += test_power.o obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o -obj-$(CONFIG_BATTERY_ANDROID) += android_battery.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c deleted file mode 100644 index 8d45ff0f3677..000000000000 --- a/drivers/power/android_battery.c +++ /dev/null @@ -1,692 +0,0 @@ -/* - * android_battery.c - * Android Battery Driver - * - * Copyright (C) 2012 Google, Inc. - * Copyright (C) 2012 Samsung Electronics - * - * Based on work by himihee.seo@samsung.com, ms925.kim@samsung.com, and - * joshua.chang@samsung.com. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define FAST_POLL (1 * 60) -#define SLOW_POLL (10 * 60) - -struct android_bat_data { - struct android_bat_platform_data *pdata; - struct android_bat_callbacks callbacks; - - struct device *dev; - - struct power_supply psy_bat; - - struct wake_lock monitor_wake_lock; - struct wake_lock charger_wake_lock; - - int charge_source; - - int batt_temp; - int batt_current; - unsigned int batt_health; - unsigned int batt_vcell; - unsigned int batt_soc; - unsigned int charging_status; - bool recharging; - unsigned long charging_start_time; - - struct workqueue_struct *monitor_wqueue; - struct work_struct monitor_work; - struct work_struct charger_work; - - struct alarm monitor_alarm; - ktime_t last_poll; - - struct dentry *debugfs_entry; -}; - -static enum power_supply_property android_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CURRENT_NOW, -}; - -static DEFINE_MUTEX(android_bat_state_lock); - -static void android_bat_update_data(struct android_bat_data *battery); -static int android_bat_enable_charging(struct android_bat_data *battery, - bool enable); - -static char *charge_source_str(int charge_source) -{ - switch (charge_source) { - case CHARGE_SOURCE_NONE: - return "none"; - case CHARGE_SOURCE_AC: - return "ac"; - case CHARGE_SOURCE_USB: - return "usb"; - default: - break; - } - - return "?"; -} - -static int android_bat_get_property(struct power_supply *ps, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct android_bat_data *battery = - container_of(ps, struct android_bat_data, psy_bat); - - switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = battery->charging_status; - break; - case POWER_SUPPLY_PROP_HEALTH: - val->intval = battery->batt_health; - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = 1; - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = battery->batt_temp; - break; - case POWER_SUPPLY_PROP_ONLINE: - val->intval = 1; - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - android_bat_update_data(battery); - val->intval = battery->batt_vcell; - if (val->intval == -1) - return -EINVAL; - break; - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = battery->batt_soc; - if (val->intval == -1) - return -EINVAL; - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; - break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - android_bat_update_data(battery); - val->intval = battery->batt_current; - break; - default: - return -EINVAL; - } - return 0; -} - -static void android_bat_get_temp(struct android_bat_data *battery) -{ - int batt_temp = 42; /* 4.2C */ - int health = battery->batt_health; - - if (battery->pdata->get_temperature) - battery->pdata->get_temperature(&batt_temp); - - if (battery->charge_source != CHARGE_SOURCE_NONE) { - if (batt_temp >= battery->pdata->temp_high_threshold) { - if (health != POWER_SUPPLY_HEALTH_OVERHEAT && - health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { - pr_info("battery overheat (%d>=%d), " \ - "charging unavailable\n", - batt_temp, - battery->pdata->temp_high_threshold); - battery->batt_health = - POWER_SUPPLY_HEALTH_OVERHEAT; - } - } else if (batt_temp <= battery->pdata->temp_high_recovery && - batt_temp >= battery->pdata->temp_low_recovery) { - if (health == POWER_SUPPLY_HEALTH_OVERHEAT || - health == POWER_SUPPLY_HEALTH_COLD) { - pr_info("battery recovery (%d,%d~%d)," \ - "charging available\n", - batt_temp, - battery->pdata->temp_low_recovery, - battery->pdata->temp_high_recovery); - battery->batt_health = - POWER_SUPPLY_HEALTH_GOOD; - } - } else if (batt_temp <= battery->pdata->temp_low_threshold) { - if (health != POWER_SUPPLY_HEALTH_COLD && - health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { - pr_info("battery cold (%d <= %d)," \ - "charging unavailable\n", - batt_temp, - battery->pdata->temp_low_threshold); - battery->batt_health = - POWER_SUPPLY_HEALTH_COLD; - } - } - } - - battery->batt_temp = batt_temp; -} - -/* - * android_bat_state_lock not held, may call back into - * android_bat_charge_source_changed. Gathering data here can be - * non-atomic; updating our state based on the data may need to be - * atomic. - */ - -static void android_bat_update_data(struct android_bat_data *battery) -{ - int ret; - int v; - - if (battery->pdata->poll_charge_source) - battery->charge_source = battery->pdata->poll_charge_source(); - - if (battery->pdata->get_voltage_now) { - ret = battery->pdata->get_voltage_now(); - battery->batt_vcell = ret >= 0 ? ret : 4242000; - } - - if (battery->pdata->get_capacity) { - ret = battery->pdata->get_capacity(); - battery->batt_soc = ret >= 0 ? ret : 42; - } - - if (battery->pdata->get_current_now) { - ret = battery->pdata->get_current_now(&v); - - if (!ret) - battery->batt_current = v; - } - - android_bat_get_temp(battery); -} - -static void android_bat_set_charge_time(struct android_bat_data *battery, - bool enable) -{ - if (enable && !battery->charging_start_time) { - struct timespec cur_time; - - get_monotonic_boottime(&cur_time); - /* record start time for charge timeout timer */ - battery->charging_start_time = cur_time.tv_sec; - } else if (!enable) { - /* clear charge timeout timer */ - battery->charging_start_time = 0; - } -} - -static int android_bat_enable_charging(struct android_bat_data *battery, - bool enable) -{ - if (enable && (battery->batt_health != POWER_SUPPLY_HEALTH_GOOD)) { - battery->charging_status = - POWER_SUPPLY_STATUS_NOT_CHARGING; - return -EPERM; - } - - if (enable) { - if (battery->pdata && battery->pdata->set_charging_current) - battery->pdata->set_charging_current - (battery->charge_source); - } - - if (battery->pdata && battery->pdata->set_charging_enable) - battery->pdata->set_charging_enable(enable); - - android_bat_set_charge_time(battery, enable); - pr_info("battery: enable=%d charger: %s\n", enable, - charge_source_str(battery->charge_source)); - return 0; -} - -static bool android_bat_charge_timeout(struct android_bat_data *battery, - unsigned long timeout) -{ - struct timespec cur_time; - - if (!battery->charging_start_time) - return 0; - - get_monotonic_boottime(&cur_time); - pr_debug("%s: Start time: %ld, End time: %ld, current time: %ld\n", - __func__, battery->charging_start_time, - battery->charging_start_time + timeout, - cur_time.tv_sec); - return cur_time.tv_sec >= battery->charging_start_time + timeout; -} - -static void android_bat_charging_timer(struct android_bat_data *battery) -{ - if (!battery->charging_start_time && - battery->charging_status == POWER_SUPPLY_STATUS_CHARGING) { - android_bat_enable_charging(battery, true); - battery->recharging = true; - pr_debug("%s: charge status charging but timer is expired\n", - __func__); - } else if (battery->charging_start_time == 0) { - pr_debug("%s: charging_start_time never initialized\n", - __func__); - return; - } - - if (android_bat_charge_timeout( - battery, - battery->recharging ? battery->pdata->recharging_time : - battery->pdata->full_charging_time)) { - android_bat_enable_charging(battery, false); - if (battery->batt_vcell > - battery->pdata->recharging_voltage && - battery->batt_soc == 100) - battery->charging_status = - POWER_SUPPLY_STATUS_FULL; - battery->recharging = false; - battery->charging_start_time = 0; - pr_info("battery: charging timer expired\n"); - } - - return; -} - -static void android_bat_charge_source_changed(struct android_bat_callbacks *ptr, - int charge_source) -{ - struct android_bat_data *battery = - container_of(ptr, struct android_bat_data, callbacks); - - wake_lock(&battery->charger_wake_lock); - mutex_lock(&android_bat_state_lock); - battery->charge_source = charge_source; - - pr_info("battery: charge source type was changed: %s\n", - charge_source_str(battery->charge_source)); - - mutex_unlock(&android_bat_state_lock); - queue_work(battery->monitor_wqueue, &battery->charger_work); -} - -static void android_bat_set_full_status(struct android_bat_callbacks *ptr) -{ - struct android_bat_data *battery = - container_of(ptr, struct android_bat_data, callbacks); - - mutex_lock(&android_bat_state_lock); - pr_info("battery: battery full\n"); - battery->charging_status = POWER_SUPPLY_STATUS_FULL; - android_bat_enable_charging(battery, false); - battery->recharging = false; - mutex_unlock(&android_bat_state_lock); - power_supply_changed(&battery->psy_bat); -} - -static void android_bat_charger_work(struct work_struct *work) -{ - struct android_bat_data *battery = - container_of(work, struct android_bat_data, charger_work); - - mutex_lock(&android_bat_state_lock); - - switch (battery->charge_source) { - case CHARGE_SOURCE_NONE: - battery->charging_status = POWER_SUPPLY_STATUS_DISCHARGING; - android_bat_enable_charging(battery, false); - battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; - battery->recharging = false; - battery->charging_start_time = 0; - break; - case CHARGE_SOURCE_USB: - case CHARGE_SOURCE_AC: - /* - * If charging status indicates a charger was already - * connected prior to this and the status is something - * other than charging ("full" or "not-charging"), leave - * the status alone. - */ - if (battery->charging_status == - POWER_SUPPLY_STATUS_DISCHARGING || - battery->charging_status == POWER_SUPPLY_STATUS_UNKNOWN) - battery->charging_status = POWER_SUPPLY_STATUS_CHARGING; - - /* - * Don't re-enable charging if the battery is full and we - * are not actively re-charging it, or if "not-charging" - * status is set. - */ - if (!((battery->charging_status == POWER_SUPPLY_STATUS_FULL - && !battery->recharging) || battery->charging_status == - POWER_SUPPLY_STATUS_NOT_CHARGING)) - android_bat_enable_charging(battery, true); - - break; - default: - pr_err("%s: Invalid charger type\n", __func__); - break; - } - - mutex_unlock(&android_bat_state_lock); - wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); - power_supply_changed(&battery->psy_bat); -} - - -static void android_bat_monitor_set_alarm(struct android_bat_data *battery, - int seconds) -{ - alarm_start(&battery->monitor_alarm, - ktime_add(battery->last_poll, ktime_set(seconds, 0))); -} - -static void android_bat_monitor_work(struct work_struct *work) -{ - struct android_bat_data *battery = - container_of(work, struct android_bat_data, monitor_work); - struct timespec cur_time; - - wake_lock(&battery->monitor_wake_lock); - android_bat_update_data(battery); - mutex_lock(&android_bat_state_lock); - - switch (battery->charging_status) { - case POWER_SUPPLY_STATUS_FULL: - if (battery->batt_vcell < battery->pdata->recharging_voltage && - !battery->recharging) { - battery->recharging = true; - android_bat_enable_charging(battery, true); - pr_info("battery: start recharging, v=%d\n", - battery->batt_vcell/1000); - } - break; - case POWER_SUPPLY_STATUS_DISCHARGING: - break; - case POWER_SUPPLY_STATUS_CHARGING: - switch (battery->batt_health) { - case POWER_SUPPLY_HEALTH_OVERHEAT: - case POWER_SUPPLY_HEALTH_COLD: - case POWER_SUPPLY_HEALTH_OVERVOLTAGE: - case POWER_SUPPLY_HEALTH_DEAD: - case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: - battery->charging_status = - POWER_SUPPLY_STATUS_NOT_CHARGING; - android_bat_enable_charging(battery, false); - - pr_info("battery: Not charging, health=%d\n", - battery->batt_health); - break; - default: - break; - } - break; - case POWER_SUPPLY_STATUS_NOT_CHARGING: - if (battery->batt_health == POWER_SUPPLY_HEALTH_GOOD) { - pr_info("battery: battery health recovered\n"); - if (battery->charge_source != CHARGE_SOURCE_NONE) { - android_bat_enable_charging(battery, true); - battery->charging_status - = POWER_SUPPLY_STATUS_CHARGING; - } else { - battery->charging_status - = POWER_SUPPLY_STATUS_DISCHARGING; - } - } - break; - default: - pr_err("%s: Undefined battery status: %d\n", __func__, - battery->charging_status); - break; - } - - android_bat_charging_timer(battery); - get_monotonic_boottime(&cur_time); - pr_info("battery: l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", - battery->batt_soc, battery->batt_vcell/1000, - battery->batt_current, battery->batt_temp < 0 ? "-" : "", - abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), - battery->batt_health, battery->charging_status, - battery->recharging ? "r" : "", - battery->charging_start_time ? - cur_time.tv_sec - battery->charging_start_time : 0, - charge_source_str(battery->charge_source)); - mutex_unlock(&android_bat_state_lock); - power_supply_changed(&battery->psy_bat); - battery->last_poll = ktime_get_boottime(); - android_bat_monitor_set_alarm(battery, FAST_POLL); - wake_unlock(&battery->monitor_wake_lock); - return; -} - -static enum alarmtimer_restart android_bat_monitor_alarm( - struct alarm *alarm, ktime_t now) -{ - struct android_bat_data *battery = - container_of(alarm, struct android_bat_data, monitor_alarm); - - wake_lock(&battery->monitor_wake_lock); - queue_work(battery->monitor_wqueue, &battery->monitor_work); - return ALARMTIMER_NORESTART; -} - -static int android_power_debug_dump(struct seq_file *s, void *unused) -{ - struct android_bat_data *battery = s->private; - struct timespec cur_time; - - android_bat_update_data(battery); - get_monotonic_boottime(&cur_time); - mutex_lock(&android_bat_state_lock); - seq_printf(s, "l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", - battery->batt_soc, battery->batt_vcell/1000, - battery->batt_current, battery->batt_temp < 0 ? "-" : "", - abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), - battery->batt_health, battery->charging_status, - battery->recharging ? "r" : "", - battery->charging_start_time ? - cur_time.tv_sec - battery->charging_start_time : 0, - charge_source_str(battery->charge_source)); - mutex_unlock(&android_bat_state_lock); - return 0; -} - -static int android_power_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, android_power_debug_dump, inode->i_private); -} - -static const struct file_operations android_power_debug_fops = { - .open = android_power_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int android_bat_probe(struct platform_device *pdev) -{ - struct android_bat_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct android_bat_data *battery; - int ret = 0; - - dev_info(&pdev->dev, "Android Battery Driver\n"); - battery = kzalloc(sizeof(*battery), GFP_KERNEL); - if (!battery) - return -ENOMEM; - - battery->pdata = pdata; - if (!battery->pdata) { - pr_err("%s : No platform data\n", __func__); - ret = -EINVAL; - goto err_pdata; - } - - battery->dev = &pdev->dev; - platform_set_drvdata(pdev, battery); - battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; - - battery->psy_bat.name = "android-battery", - battery->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY, - battery->psy_bat.properties = android_battery_props, - battery->psy_bat.num_properties = ARRAY_SIZE(android_battery_props), - battery->psy_bat.get_property = android_bat_get_property, - - battery->batt_vcell = -1; - battery->batt_soc = -1; - - wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND, - "android-battery-monitor"); - wake_lock_init(&battery->charger_wake_lock, WAKE_LOCK_SUSPEND, - "android-chargerdetect"); - - ret = power_supply_register(&pdev->dev, &battery->psy_bat); - if (ret) { - dev_err(battery->dev, "%s: failed to register psy_bat\n", - __func__); - goto err_psy_bat_reg; - } - - battery->monitor_wqueue = - alloc_workqueue(dev_name(&pdev->dev), WQ_FREEZABLE, 1); - if (!battery->monitor_wqueue) { - dev_err(battery->dev, "%s: fail to create workqueue\n", - __func__); - goto err_wq; - } - - INIT_WORK(&battery->monitor_work, android_bat_monitor_work); - INIT_WORK(&battery->charger_work, android_bat_charger_work); - - battery->callbacks.charge_source_changed = - android_bat_charge_source_changed; - battery->callbacks.battery_set_full = - android_bat_set_full_status; - if (battery->pdata && battery->pdata->register_callbacks) - battery->pdata->register_callbacks(&battery->callbacks); - - /* get initial charger status */ - if (battery->pdata->poll_charge_source) - battery->charge_source = battery->pdata->poll_charge_source(); - - wake_lock(&battery->charger_wake_lock); - queue_work(battery->monitor_wqueue, &battery->charger_work); - - wake_lock(&battery->monitor_wake_lock); - battery->last_poll = ktime_get_boottime(); - alarm_init(&battery->monitor_alarm, ALARM_BOOTTIME, - android_bat_monitor_alarm); - queue_work(battery->monitor_wqueue, &battery->monitor_work); - - battery->debugfs_entry = - debugfs_create_file("android-power", S_IRUGO, NULL, - battery, &android_power_debug_fops); - if (!battery->debugfs_entry) - pr_err("failed to create android-power debugfs entry\n"); - - return 0; - -err_wq: - power_supply_unregister(&battery->psy_bat); -err_psy_bat_reg: - wake_lock_destroy(&battery->monitor_wake_lock); - wake_lock_destroy(&battery->charger_wake_lock); -err_pdata: - kfree(battery); - - return ret; -} - -static int android_bat_remove(struct platform_device *pdev) -{ - struct android_bat_data *battery = platform_get_drvdata(pdev); - - alarm_cancel(&battery->monitor_alarm); - flush_workqueue(battery->monitor_wqueue); - destroy_workqueue(battery->monitor_wqueue); - power_supply_unregister(&battery->psy_bat); - wake_lock_destroy(&battery->monitor_wake_lock); - wake_lock_destroy(&battery->charger_wake_lock); - debugfs_remove(battery->debugfs_entry); - kfree(battery); - return 0; -} - -static int android_bat_suspend(struct device *dev) -{ - struct android_bat_data *battery = dev_get_drvdata(dev); - - cancel_work_sync(&battery->monitor_work); - android_bat_monitor_set_alarm( - battery, - battery->charge_source == CHARGE_SOURCE_NONE ? - SLOW_POLL : FAST_POLL); - return 0; -} - -static void android_bat_resume(struct device *dev) -{ - struct android_bat_data *battery = dev_get_drvdata(dev); - - android_bat_monitor_set_alarm(battery, FAST_POLL); - return; -} - -static const struct dev_pm_ops android_bat_pm_ops = { - .prepare = android_bat_suspend, - .complete = android_bat_resume, -}; - -static struct platform_driver android_bat_driver = { - .driver = { - .name = "android-battery", - .owner = THIS_MODULE, - .pm = &android_bat_pm_ops, - }, - .probe = android_bat_probe, - .remove = android_bat_remove, -}; - -static int __init android_bat_init(void) -{ - return platform_driver_register(&android_bat_driver); -} - -static void __exit android_bat_exit(void) -{ - platform_driver_unregister(&android_bat_driver); -} - -late_initcall(android_bat_init); -module_exit(android_bat_exit); - -MODULE_DESCRIPTION("Android battery driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/android_battery.h b/include/linux/platform_data/android_battery.h deleted file mode 100644 index f6c8298fd885..000000000000 --- a/include/linux/platform_data/android_battery.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * android_battery.h - * - * Copyright (C) 2012 Samsung Electronics - * - * 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. - */ - -#ifndef _LINUX_ANDROID_BATTERY_H -#define _LINUX_ANDROID_BATTERY_H - -enum { - CHARGE_SOURCE_NONE = 0, - CHARGE_SOURCE_AC, - CHARGE_SOURCE_USB, -}; - -struct android_bat_callbacks { - void (*charge_source_changed) - (struct android_bat_callbacks *, int); - void (*battery_set_full)(struct android_bat_callbacks *); -}; - -struct android_bat_platform_data { - void (*register_callbacks)(struct android_bat_callbacks *); - void (*unregister_callbacks)(void); - void (*set_charging_current) (int); - void (*set_charging_enable) (int); - int (*poll_charge_source) (void); - int (*get_capacity) (void); - int (*get_temperature) (int *); - int (*get_voltage_now)(void); - int (*get_current_now)(int *); - - int temp_high_threshold; - int temp_high_recovery; - int temp_low_recovery; - int temp_low_threshold; - - unsigned long full_charging_time; - unsigned long recharging_time; - unsigned int recharging_voltage; -}; - -#endif From 6ebfe5864ae65ad31e1e5526f0f45a5f735c9a5b Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 26 Jun 2013 17:26:01 -0700 Subject: [PATCH 07/49] mm: add a field to store names for private anonymous memory Userspace processes often have multiple allocators that each do anonymous mmaps to get memory. When examining memory usage of individual processes or systems as a whole, it is useful to be able to break down the various heaps that were allocated by each layer and examine their size, RSS, and physical memory usage. This patch adds a user pointer to the shared union in vm_area_struct that points to a null terminated string inside the user process containing a name for the vma. vmas that point to the same address will be merged, but vmas that point to equivalent strings at different addresses will not be merged. Userspace can set the name for a region of memory by calling prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start, len, (unsigned long)name); Setting the name to NULL clears it. The names of named anonymous vmas are shown in /proc/pid/maps as [anon:] and in /proc/pid/smaps in a new "Name" field that is only present for named vmas. If the userspace pointer is no longer valid all or part of the name will be replaced with "". The idea to store a userspace pointer to reduce the complexity within mm (at the expense of the complexity of reading /proc/pid/mem) came from Dave Hansen. This results in no runtime overhead in the mm subsystem other than comparing the anon_name pointers when considering vma merging. The pointer is stored in a union with fieds that are only used on file-backed mappings, so it does not increase memory usage. Change-Id: Ie2ffc0967d4ffe7ee4c70781313c7b00cf7e3092 Signed-off-by: Colin Cross --- Documentation/filesystems/proc.txt | 6 ++ fs/proc/task_mmu.c | 62 ++++++++++++ include/linux/mm.h | 2 +- include/linux/mm_types.h | 15 +++ include/uapi/linux/prctl.h | 3 + kernel/sys.c | 145 +++++++++++++++++++++++++++++ mm/madvise.c | 3 +- mm/mempolicy.c | 2 +- mm/mlock.c | 3 +- mm/mmap.c | 44 +++++---- mm/mprotect.c | 3 +- 11 files changed, 265 insertions(+), 23 deletions(-) diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index fd8d0d594fc7..e0eb9d287312 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -369,6 +369,8 @@ is not associated with a file: [stack:1001] = the stack of the thread with tid 1001 [vdso] = the "virtual dynamic shared object", the kernel system call handler + [anon:] = an anonymous mapping that has been + named by userspace or if empty, the mapping is anonymous. @@ -419,6 +421,7 @@ KernelPageSize: 4 kB MMUPageSize: 4 kB Locked: 374 kB VmFlags: rd ex mr mw me de +Name: name from userspace the first of these lines shows the same information as is displayed for the mapping in /proc/PID/maps. The remaining lines show the size of the mapping @@ -469,6 +472,9 @@ Note that there is no guarantee that every flag and associated mnemonic will be present in all further kernel releases. Things get changed, the flags may be vanished or the reverse -- new added. +The "Name" field will only be present on a mapping that has been named by +userspace, and will show the name passed in by userspace. + This file is only present if the CONFIG_MMU kernel configuration option is enabled. diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 3e636d864d56..e2c925fa7827 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -134,6 +134,56 @@ static void release_task_mempolicy(struct proc_maps_private *priv) } #endif +static void seq_print_vma_name(struct seq_file *m, struct vm_area_struct *vma) +{ + const char __user *name = vma_get_anon_name(vma); + struct mm_struct *mm = vma->vm_mm; + + unsigned long page_start_vaddr; + unsigned long page_offset; + unsigned long num_pages; + unsigned long max_len = NAME_MAX; + int i; + + page_start_vaddr = (unsigned long)name & PAGE_MASK; + page_offset = (unsigned long)name - page_start_vaddr; + num_pages = DIV_ROUND_UP(page_offset + max_len, PAGE_SIZE); + + seq_puts(m, "[anon:"); + + for (i = 0; i < num_pages; i++) { + int len; + int write_len; + const char *kaddr; + long pages_pinned; + struct page *page; + + pages_pinned = get_user_pages(current, mm, page_start_vaddr, + 1, 0, 0, &page, NULL); + if (pages_pinned < 1) { + seq_puts(m, "]"); + return; + } + + kaddr = (const char *)kmap(page); + len = min(max_len, PAGE_SIZE - page_offset); + write_len = strnlen(kaddr + page_offset, len); + seq_write(m, kaddr + page_offset, write_len); + kunmap(page); + put_page(page); + + /* if strnlen hit a null terminator then we're done */ + if (write_len != len) + break; + + max_len -= len; + page_offset = 0; + page_start_vaddr += PAGE_SIZE; + } + + seq_putc(m, ']'); +} + static void vma_stop(struct proc_maps_private *priv, struct vm_area_struct *vma) { if (vma && vma != priv->tail_vma) { @@ -335,6 +385,12 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) pad_len_spaces(m, len); seq_printf(m, "[stack:%d]", tid); } + goto done; + } + + if (vma_get_anon_name(vma)) { + pad_len_spaces(m, len); + seq_print_vma_name(m, vma); } } @@ -634,6 +690,12 @@ static int show_smap(struct seq_file *m, void *v, int is_pid) show_smap_vma_flags(m, vma); + if (vma_get_anon_name(vma)) { + seq_puts(m, "Name: "); + seq_print_vma_name(m, vma); + seq_putc(m, '\n'); + } + if (m->count < m->size) /* vma is copied successfully */ m->version = (vma != get_gate_vma(task->mm)) ? vma->vm_start : 0; diff --git a/include/linux/mm.h b/include/linux/mm.h index bd5679ddcd3f..01eb01df9225 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1486,7 +1486,7 @@ extern int vma_adjust(struct vm_area_struct *vma, unsigned long start, extern struct vm_area_struct *vma_merge(struct mm_struct *, struct vm_area_struct *prev, unsigned long addr, unsigned long end, unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t, - struct mempolicy *); + struct mempolicy *, const char __user *); extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *); extern int split_vma(struct mm_struct *, struct vm_area_struct *, unsigned long addr, int new_below); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index ace9a5f01c64..875ba48dd72b 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -255,6 +255,10 @@ struct vm_area_struct { * For areas with an address space and backing store, * linkage into the address_space->i_mmap interval tree, or * linkage of vma in the address_space->i_mmap_nonlinear list. + * + * For private anonymous mappings, a pointer to a null terminated string + * in the user process containing the name given to the vma, or NULL + * if unnamed. */ union { struct { @@ -262,6 +266,7 @@ struct vm_area_struct { unsigned long rb_subtree_last; } linear; struct list_head nonlinear; + const char __user *anon_name; } shared; /* @@ -456,4 +461,14 @@ static inline cpumask_t *mm_cpumask(struct mm_struct *mm) return mm->cpu_vm_mask_var; } + +/* Return the name for an anonymous mapping or NULL for a file-backed mapping */ +static inline const char __user *vma_get_anon_name(struct vm_area_struct *vma) +{ + if (vma->vm_file) + return NULL; + + return vma->shared.anon_name; +} + #endif /* _LINUX_MM_TYPES_H */ diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index 289760f424aa..253856a2a8ad 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -149,4 +149,7 @@ #define PR_GET_TID_ADDRESS 40 +#define PR_SET_VMA 0x53564d41 +# define PR_SET_VMA_ANON_NAME 0 + #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/sys.c b/kernel/sys.c index 2bbd9a73b54c..1c9090bc6746 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include @@ -2099,6 +2101,146 @@ static int prctl_get_tid_address(struct task_struct *me, int __user **tid_addr) } #endif + +static int prctl_update_vma_anon_name(struct vm_area_struct *vma, + struct vm_area_struct **prev, + unsigned long start, unsigned long end, + const char __user *name_addr) +{ + struct mm_struct * mm = vma->vm_mm; + int error = 0; + pgoff_t pgoff; + + if (name_addr == vma_get_anon_name(vma)) { + *prev = vma; + goto out; + } + + pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); + *prev = vma_merge(mm, *prev, start, end, vma->vm_flags, vma->anon_vma, + vma->vm_file, pgoff, vma_policy(vma), + name_addr); + if (*prev) { + vma = *prev; + goto success; + } + + *prev = vma; + + if (start != vma->vm_start) { + error = split_vma(mm, vma, start, 1); + if (error) + goto out; + } + + if (end != vma->vm_end) { + error = split_vma(mm, vma, end, 0); + if (error) + goto out; + } + +success: + if (!vma->vm_file) + vma->shared.anon_name = name_addr; + +out: + if (error == -ENOMEM) + error = -EAGAIN; + return error; +} + +static int prctl_set_vma_anon_name(unsigned long start, unsigned long end, + unsigned long arg) +{ + unsigned long tmp; + struct vm_area_struct * vma, *prev; + int unmapped_error = 0; + int error = -EINVAL; + + /* + * If the interval [start,end) covers some unmapped address + * ranges, just ignore them, but return -ENOMEM at the end. + * - this matches the handling in madvise. + */ + vma = find_vma_prev(current->mm, start, &prev); + if (vma && start > vma->vm_start) + prev = vma; + + for (;;) { + /* Still start < end. */ + error = -ENOMEM; + if (!vma) + return error; + + /* Here start < (end|vma->vm_end). */ + if (start < vma->vm_start) { + unmapped_error = -ENOMEM; + start = vma->vm_start; + if (start >= end) + return error; + } + + /* Here vma->vm_start <= start < (end|vma->vm_end) */ + tmp = vma->vm_end; + if (end < tmp) + tmp = end; + + /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */ + error = prctl_update_vma_anon_name(vma, &prev, start, end, + (const char __user *)arg); + if (error) + return error; + start = tmp; + if (prev && start < prev->vm_end) + start = prev->vm_end; + error = unmapped_error; + if (start >= end) + return error; + if (prev) + vma = prev->vm_next; + else /* madvise_remove dropped mmap_sem */ + vma = find_vma(current->mm, start); + } +} + +static int prctl_set_vma(unsigned long opt, unsigned long start, + unsigned long len_in, unsigned long arg) +{ + struct mm_struct *mm = current->mm; + int error; + unsigned long len; + unsigned long end; + + if (start & ~PAGE_MASK) + return -EINVAL; + len = (len_in + ~PAGE_MASK) & PAGE_MASK; + + /* Check to see whether len was rounded up from small -ve to zero */ + if (len_in && !len) + return -EINVAL; + + end = start + len; + if (end < start) + return -EINVAL; + + if (end == start) + return 0; + + down_write(&mm->mmap_sem); + + switch (opt) { + case PR_SET_VMA_ANON_NAME: + error = prctl_set_vma_anon_name(start, end, arg); + break; + default: + error = -EINVAL; + } + + up_write(&mm->mmap_sem); + + return error; +} + SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, unsigned long, arg4, unsigned long, arg5) { @@ -2226,6 +2368,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, else return -EINVAL; break; + case PR_SET_VMA: + error = prctl_set_vma(arg2, arg3, arg4, arg5); + break; default: return -EINVAL; } diff --git a/mm/madvise.c b/mm/madvise.c index 7055883e6e25..85d1a5427c27 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -102,7 +102,8 @@ static long madvise_behavior(struct vm_area_struct * vma, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *prev = vma_merge(mm, *prev, start, end, new_flags, vma->anon_vma, - vma->vm_file, pgoff, vma_policy(vma)); + vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (*prev) { vma = *prev; goto success; diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 74310017296e..7b349aafe79d 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -728,7 +728,7 @@ static int mbind_range(struct mm_struct *mm, unsigned long start, ((vmstart - vma->vm_start) >> PAGE_SHIFT); prev = vma_merge(mm, prev, vmstart, vmend, vma->vm_flags, vma->anon_vma, vma->vm_file, pgoff, - new_pol); + new_pol, vma_get_anon_name(name)); if (prev) { vma = prev; next = vma->vm_next; diff --git a/mm/mlock.c b/mm/mlock.c index 79b7cf7d1bca..33861c780070 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -287,7 +287,8 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma, - vma->vm_file, pgoff, vma_policy(vma)); + vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (*prev) { vma = *prev; goto success; diff --git a/mm/mmap.c b/mm/mmap.c index f681e1842fad..25abb881c9e9 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -893,7 +893,8 @@ again: remove_next = 1 + (end > next->vm_end); * per-vma resources, so we don't attempt to merge those. */ static inline int is_mergeable_vma(struct vm_area_struct *vma, - struct file *file, unsigned long vm_flags) + struct file *file, unsigned long vm_flags, + const char __user *anon_name) { if (vma->vm_flags ^ vm_flags) return 0; @@ -901,6 +902,8 @@ static inline int is_mergeable_vma(struct vm_area_struct *vma, return 0; if (vma->vm_ops && vma->vm_ops->close) return 0; + if (vma_get_anon_name(vma) != anon_name) + return 0; return 1; } @@ -931,9 +934,10 @@ static inline int is_mergeable_anon_vma(struct anon_vma *anon_vma1, */ static int can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, - struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) + struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, + const char __user *anon_name) { - if (is_mergeable_vma(vma, file, vm_flags) && + if (is_mergeable_vma(vma, file, vm_flags, anon_name) && is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { if (vma->vm_pgoff == vm_pgoff) return 1; @@ -950,9 +954,10 @@ can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, */ static int can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, - struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) + struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, + const char __user *anon_name) { - if (is_mergeable_vma(vma, file, vm_flags) && + if (is_mergeable_vma(vma, file, vm_flags, anon_name) && is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { pgoff_t vm_pglen; vm_pglen = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; @@ -963,9 +968,9 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, } /* - * Given a mapping request (addr,end,vm_flags,file,pgoff), figure out - * whether that can be merged with its predecessor or its successor. - * Or both (it neatly fills a hole). + * Given a mapping request (addr,end,vm_flags,file,pgoff,anon_name), + * figure out whether that can be merged with its predecessor or its + * successor. Or both (it neatly fills a hole). * * In most cases - when called for mmap, brk or mremap - [addr,end) is * certain not to be mapped by the time vma_merge is called; but when @@ -995,7 +1000,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, struct vm_area_struct *prev, unsigned long addr, unsigned long end, unsigned long vm_flags, struct anon_vma *anon_vma, struct file *file, - pgoff_t pgoff, struct mempolicy *policy) + pgoff_t pgoff, struct mempolicy *policy, + const char __user *anon_name) { pgoff_t pglen = (end - addr) >> PAGE_SHIFT; struct vm_area_struct *area, *next; @@ -1021,15 +1027,15 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, */ if (prev && prev->vm_end == addr && mpol_equal(vma_policy(prev), policy) && - can_vma_merge_after(prev, vm_flags, - anon_vma, file, pgoff)) { + can_vma_merge_after(prev, vm_flags, anon_vma, + file, pgoff, anon_name)) { /* * OK, it can. Can we now merge in the successor as well? */ if (next && end == next->vm_start && mpol_equal(policy, vma_policy(next)) && - can_vma_merge_before(next, vm_flags, - anon_vma, file, pgoff+pglen) && + can_vma_merge_before(next, vm_flags, anon_vma, + file, pgoff+pglen, anon_name) && is_mergeable_anon_vma(prev->anon_vma, next->anon_vma, NULL)) { /* cases 1, 6 */ @@ -1049,8 +1055,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, */ if (next && end == next->vm_start && mpol_equal(policy, vma_policy(next)) && - can_vma_merge_before(next, vm_flags, - anon_vma, file, pgoff+pglen)) { + can_vma_merge_before(next, vm_flags, anon_vma, + file, pgoff+pglen, anon_name)) { if (prev && addr < prev->vm_end) /* case 4 */ err = vma_adjust(prev, prev->vm_start, addr, prev->vm_pgoff, NULL); @@ -1519,7 +1525,8 @@ munmap_back: /* * Can we just expand an old mapping? */ - vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, NULL); + vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, + NULL, NULL); if (vma) goto out; @@ -2663,7 +2670,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len) /* Can we just expand an old private anonymous mapping? */ vma = vma_merge(mm, prev, addr, addr + len, flags, - NULL, NULL, pgoff, NULL); + NULL, NULL, pgoff, NULL, NULL); if (vma) goto out; @@ -2821,7 +2828,8 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, if (find_vma_links(mm, addr, addr + len, &prev, &rb_link, &rb_parent)) return NULL; /* should never get here */ new_vma = vma_merge(mm, prev, addr, addr + len, vma->vm_flags, - vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma)); + vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (new_vma) { /* * Source vma may have been merged into new_vma diff --git a/mm/mprotect.c b/mm/mprotect.c index 94722a4d6b43..94d50b7a6dad 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -271,7 +271,8 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, */ pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *pprev = vma_merge(mm, *pprev, start, end, newflags, - vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma)); + vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (*pprev) { vma = *pprev; goto success; From f526a55b400d166dafc6062e5818a2410555ef39 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Aug 2013 14:03:55 -0700 Subject: [PATCH 08/49] ion: chunk_heap: fix leak in allocated counter buffer->size is controlled by the outer ion layer, don't modify it inside the heap. Instead, compute the rounded up allocated size on demand. Change-Id: I288ffc1221ce96cfe2591468502ac3279065bde4 Signed-off-by: Colin Cross --- drivers/gpu/ion/ion_chunk_heap.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/ion/ion_chunk_heap.c b/drivers/gpu/ion/ion_chunk_heap.c index d5e7379a6f46..15c9d7ad2502 100644 --- a/drivers/gpu/ion/ion_chunk_heap.c +++ b/drivers/gpu/ion/ion_chunk_heap.c @@ -47,15 +47,15 @@ static int ion_chunk_heap_allocate(struct ion_heap *heap, struct scatterlist *sg; int ret, i; unsigned long num_chunks; + unsigned long allocated_size; if (ion_buffer_fault_user_mappings(buffer)) return -ENOMEM; - num_chunks = ALIGN(size, chunk_heap->chunk_size) / - chunk_heap->chunk_size; - buffer->size = num_chunks * chunk_heap->chunk_size; + allocated_size = ALIGN(size, chunk_heap->chunk_size); + num_chunks = allocated_size / chunk_heap->chunk_size; - if (buffer->size > chunk_heap->size - chunk_heap->allocated) + if (allocated_size > chunk_heap->size - chunk_heap->allocated) return -ENOMEM; table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); @@ -78,7 +78,7 @@ static int ion_chunk_heap_allocate(struct ion_heap *heap, } buffer->priv_virt = table; - chunk_heap->allocated += buffer->size; + chunk_heap->allocated += allocated_size; return 0; err: sg = table->sgl; @@ -100,6 +100,9 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer) struct sg_table *table = buffer->priv_virt; struct scatterlist *sg; int i; + unsigned long allocated_size; + + allocated_size = ALIGN(buffer->size, chunk_heap->chunk_size); ion_heap_buffer_zero(buffer); @@ -111,7 +114,7 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer) gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)), sg_dma_len(sg)); } - chunk_heap->allocated -= buffer->size; + chunk_heap->allocated -= allocated_size; sg_free_table(table); kfree(table); } From a3041cac9fc117df010e66ce40091fa2c4f646f8 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Aug 2013 14:10:19 -0700 Subject: [PATCH 09/49] ion: add free list size to heap debug files Change-Id: I3c6309afdbd661a2f870fd1ba3fea9543e229882 Signed-off-by: Colin Cross --- drivers/gpu/ion/ion.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index dbe5bbce6a54..48652ddd4948 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -1286,6 +1286,9 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) seq_printf(s, "%16.s %16u\n", "total orphaned", total_orphaned_size); seq_printf(s, "%16.s %16u\n", "total ", total_size); + if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) + seq_printf(s, "%16.s %16u\n", "deferred free", + heap->free_list_size); seq_printf(s, "----------------------------------------------------\n"); if (heap->debug_show) From d37aa151f874563a35d49a0ff0a129ef332c5e9e Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 21 Aug 2013 12:50:37 -0700 Subject: [PATCH 10/49] ion: convert map_kernel to return ERR_PTR ion is going to stop accepting NULL as an error value, use ERR_PTR. Change-Id: I030e8b72138904e38a4a5d225beaaa98427651fb Signed-off-by: Colin Cross --- drivers/gpu/ion/ion_carveout_heap.c | 7 ++++++- drivers/gpu/ion/ion_heap.c | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c index ce8d311968f6..86f35545eaf7 100644 --- a/drivers/gpu/ion/ion_carveout_heap.c +++ b/drivers/gpu/ion/ion_carveout_heap.c @@ -112,13 +112,18 @@ void ion_carveout_heap_unmap_dma(struct ion_heap *heap, void *ion_carveout_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer) { + void *ret; int mtype = MT_MEMORY_NONCACHED; if (buffer->flags & ION_FLAG_CACHED) mtype = MT_MEMORY; - return __arm_ioremap(buffer->priv_phys, buffer->size, + ret = __arm_ioremap(buffer->priv_phys, buffer->size, mtype); + if (ret == NULL) + return ERR_PTR(-ENOMEM); + + return ret; } void ion_carveout_heap_unmap_kernel(struct ion_heap *heap, diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c index bf6a383bd98b..786302de7ed0 100644 --- a/drivers/gpu/ion/ion_heap.c +++ b/drivers/gpu/ion/ion_heap.c @@ -56,6 +56,9 @@ void *ion_heap_map_kernel(struct ion_heap *heap, vaddr = vmap(pages, npages, VM_MAP, pgprot); vfree(pages); + if (vaddr == NULL) + return ERR_PTR(-ENOMEM); + return vaddr; } From 846b79b79391558e07b088193e14affe89d3be1b Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 20 Aug 2013 21:31:34 -0700 Subject: [PATCH 11/49] ion: remove IS_ERR_OR_NULL IS_ERR_OR_NULL is often part of a bad pattern that can accidentally return 0 on error: if (IS_ERR_OR_NULL(ptr)) return PTR_ERR(ptr); It also usually means that the errors of a function are not well defined. Replace all uses in ion.c by ensure that the return type of any function in ion is an ERR_PTR. Specify that the expected return value from map_kernel or map_dma heap ops is ERR_PTR, and warn if a heap returns NULL. Change-Id: I6e7ea0d2e62fa08d4e372a7ef6da649f7a62289c Signed-off-by: Colin Cross --- drivers/gpu/ion/ion.c | 28 +++++++++++++++------------- drivers/gpu/ion/ion_priv.h | 3 +++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index 48652ddd4948..d6700d0eb3fa 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -197,7 +197,9 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, buffer->size = len; table = heap->ops->map_dma(heap, buffer); - if (IS_ERR_OR_NULL(table)) { + if (WARN_ONCE(table == NULL, "heap->ops->map_dma should return ERR_PTR on error")) + table = ERR_PTR(-EINVAL); + if (IS_ERR(table)) { heap->ops->free(buffer); kfree(buffer); return ERR_PTR(PTR_ERR(table)); @@ -389,7 +391,7 @@ static struct ion_handle *ion_handle_lookup(struct ion_client *client, if (handle->buffer == buffer) return handle; } - return NULL; + return ERR_PTR(-EINVAL); } static bool ion_handle_validate(struct ion_client *client, struct ion_handle *handle) @@ -459,7 +461,7 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, if (!((1 << heap->id) & heap_id_mask)) continue; buffer = ion_buffer_create(heap, dev, len, align, flags); - if (!IS_ERR_OR_NULL(buffer)) + if (!IS_ERR(buffer)) break; } up_read(&dev->lock); @@ -543,7 +545,9 @@ static void *ion_buffer_kmap_get(struct ion_buffer *buffer) return buffer->vaddr; } vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer); - if (IS_ERR_OR_NULL(vaddr)) + if (WARN_ONCE(vaddr == NULL, "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++; @@ -560,7 +564,7 @@ static void *ion_handle_kmap_get(struct ion_handle *handle) return buffer->vaddr; } vaddr = ion_buffer_kmap_get(buffer); - if (IS_ERR_OR_NULL(vaddr)) + if (IS_ERR(vaddr)) return vaddr; handle->kmap_cnt++; return vaddr; @@ -954,8 +958,6 @@ static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, size_t start, mutex_unlock(&buffer->lock); if (IS_ERR(vaddr)) return PTR_ERR(vaddr); - if (!vaddr) - return -ENOMEM; return 0; } @@ -1034,7 +1036,7 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) struct ion_handle *handle; dmabuf = dma_buf_get(fd); - if (IS_ERR_OR_NULL(dmabuf)) + if (IS_ERR(dmabuf)) return ERR_PTR(PTR_ERR(dmabuf)); /* if this memory came from ion */ @@ -1049,12 +1051,12 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) mutex_lock(&client->lock); /* if a handle exists for this buffer just take a reference to it */ handle = ion_handle_lookup(client, buffer); - if (!IS_ERR_OR_NULL(handle)) { + if (!IS_ERR(handle)) { ion_handle_get(handle); goto end; } handle = ion_handle_create(client, buffer); - if (IS_ERR_OR_NULL(handle)) + if (IS_ERR(handle)) goto end; ion_handle_add(client, handle); end: @@ -1070,7 +1072,7 @@ static int ion_sync_for_device(struct ion_client *client, int fd) struct ion_buffer *buffer; dmabuf = dma_buf_get(fd); - if (IS_ERR_OR_NULL(dmabuf)) + if (IS_ERR(dmabuf)) return PTR_ERR(dmabuf); /* if this memory came from ion */ @@ -1204,7 +1206,7 @@ static int ion_open(struct inode *inode, struct file *file) pr_debug("%s: %d\n", __func__, __LINE__); client = ion_client_create(dev, "user"); - if (IS_ERR_OR_NULL(client)) + if (IS_ERR(client)) return PTR_ERR(client); file->private_data = client; @@ -1400,7 +1402,7 @@ struct ion_device *ion_device_create(long (*custom_ioctl) } idev->debug_root = debugfs_create_dir("ion", NULL); - if (IS_ERR_OR_NULL(idev->debug_root)) + if (!idev->debug_root) pr_err("ion: failed to create debug files.\n"); idev->custom_ioctl = custom_ioctl; diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index 1155994fc8ec..32461e946735 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -93,6 +93,9 @@ void ion_buffer_destroy(struct ion_buffer *buffer); * @map_kernel map memory to the kernel * @unmap_kernel unmap memory to the kernel * @map_user map memory to userspace + * + * allocate, phys, and map_user return 0 on success, -errno on error. + * map_dma and map_kernel return pointer on success, ERR_PTR on error. */ struct ion_heap_ops { int (*allocate) (struct ion_heap *heap, From 1126d98d8e34d771afcc72ce5d1c37ad4d474402 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 20 Aug 2013 22:59:41 -0700 Subject: [PATCH 12/49] ion: replace userspace handle cookies with idr Userspace handles should not leak kernel virtual addresses to userspace. They have to be validated by looking them up in an rbtree anyways, so replace them with an idr and validate them by using idr_find to convert the id number to the struct ion_handle pointer. Change-Id: Iab5667ba6f3a73256fec3949b23a9a6f8e14a283 Signed-off-by: Colin Cross --- drivers/gpu/ion/ion.c | 109 ++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index d6700d0eb3fa..8d43ad12ea6d 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "ion_priv.h" @@ -64,6 +65,7 @@ struct ion_device { * @node: node in the tree of all clients * @dev: backpointer to ion device * @handles: an rb tree of all the handles in this client + * @idr: an idr space for allocating handle ids * @lock: lock protecting the tree of handles * @name: used for debugging * @task: used for debugging @@ -76,6 +78,7 @@ struct ion_client { struct rb_node node; struct ion_device *dev; struct rb_root handles; + struct idr idr; struct mutex lock; const char *name; struct task_struct *task; @@ -90,7 +93,7 @@ struct ion_client { * @buffer: pointer to the buffer * @node: node in the client's handle rbtree * @kmap_cnt: count of times this client has mapped to kernel - * @dmap_cnt: count of times this client has mapped for dma + * @id: client-unique id allocated by client->idr * * Modifications to node, map_cnt or mapping should be protected by the * lock in the client. Other fields are never changed after initialization. @@ -101,6 +104,7 @@ struct ion_handle { struct ion_buffer *buffer; struct rb_node node; unsigned int kmap_cnt; + int id; }; bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer) @@ -356,6 +360,7 @@ static void ion_handle_destroy(struct kref *kref) ion_handle_kmap_put(handle); mutex_unlock(&buffer->lock); + idr_remove(&client->idr, handle->id); if (!RB_EMPTY_NODE(&handle->node)) rb_erase(&handle->node, &client->handles); @@ -387,36 +392,42 @@ static struct ion_handle *ion_handle_lookup(struct ion_client *client, for (n = rb_first(&client->handles); n; n = rb_next(n)) { struct ion_handle *handle = rb_entry(n, struct ion_handle, - node); + node); if (handle->buffer == buffer) return handle; } return ERR_PTR(-EINVAL); } -static bool ion_handle_validate(struct ion_client *client, struct ion_handle *handle) +static struct ion_handle *ion_uhandle_get(struct ion_client *client, int id) { - struct rb_node *n = client->handles.rb_node; - - while (n) { - struct ion_handle *handle_node = rb_entry(n, struct ion_handle, - node); - if (handle < handle_node) - n = n->rb_left; - else if (handle > handle_node) - n = n->rb_right; - else - return true; - } - return false; + return idr_find(&client->idr, id); } -static void ion_handle_add(struct ion_client *client, struct ion_handle *handle) +static bool ion_handle_validate(struct ion_client *client, struct ion_handle *handle) { + return (ion_uhandle_get(client, handle->id) == handle); +} + +static int ion_handle_add(struct ion_client *client, struct ion_handle *handle) +{ + int rc; struct rb_node **p = &client->handles.rb_node; struct rb_node *parent = NULL; struct ion_handle *entry; + do { + int id; + rc = idr_pre_get(&client->idr, GFP_KERNEL); + if (!rc) + return -ENOMEM; + rc = idr_get_new(&client->idr, handle, &id); + handle->id = id; + } while (rc == -EAGAIN); + + if (rc < 0) + return rc; + while (*p) { parent = *p; entry = rb_entry(parent, struct ion_handle, node); @@ -431,6 +442,8 @@ static void ion_handle_add(struct ion_client *client, struct ion_handle *handle) rb_link_node(&handle->node, parent, p); rb_insert_color(&handle->node, &client->handles); + + return 0; } struct ion_handle *ion_alloc(struct ion_client *client, size_t len, @@ -441,6 +454,7 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, struct ion_device *dev = client->dev; struct ion_buffer *buffer = NULL; struct ion_heap *heap; + int ret; pr_debug("%s: len %d align %d heap_id_mask %u flags %x\n", __func__, len, align, heap_id_mask, flags); @@ -480,12 +494,16 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, */ ion_buffer_put(buffer); - if (!IS_ERR(handle)) { - mutex_lock(&client->lock); - ion_handle_add(client, handle); - mutex_unlock(&client->lock); - } + if (IS_ERR(handle)) + return handle; + mutex_lock(&client->lock); + ret = ion_handle_add(client, handle); + if (ret) { + ion_handle_put(handle); + handle = ERR_PTR(ret); + } + mutex_unlock(&client->lock); return handle; } @@ -705,6 +723,7 @@ struct ion_client *ion_client_create(struct ion_device *dev, client->dev = dev; client->handles = RB_ROOT; + idr_init(&client->idr); mutex_init(&client->lock); client->name = name; client->task = task; @@ -745,6 +764,10 @@ void ion_client_destroy(struct ion_client *client) node); ion_handle_destroy(&handle->ref); } + + idr_remove_all(&client->idr); + idr_destroy(&client->idr); + down_write(&dev->lock); if (client->task) put_task_struct(client->task); @@ -1034,6 +1057,7 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) struct dma_buf *dmabuf; struct ion_buffer *buffer; struct ion_handle *handle; + int ret; dmabuf = dma_buf_get(fd); if (IS_ERR(dmabuf)) @@ -1058,7 +1082,11 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) handle = ion_handle_create(client, buffer); if (IS_ERR(handle)) goto end; - ion_handle_add(client, handle); + ret = ion_handle_add(client, handle); + if (ret) { + ion_handle_put(handle); + handle = ERR_PTR(ret); + } end: mutex_unlock(&client->lock); dma_buf_put(dmabuf); @@ -1098,17 +1126,20 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case ION_IOC_ALLOC: { struct ion_allocation_data data; + struct ion_handle *handle; if (copy_from_user(&data, (void __user *)arg, sizeof(data))) return -EFAULT; - data.handle = ion_alloc(client, data.len, data.align, + handle = ion_alloc(client, data.len, data.align, data.heap_id_mask, data.flags); - if (IS_ERR(data.handle)) - return PTR_ERR(data.handle); + if (IS_ERR(handle)) + return PTR_ERR(handle); + + data.handle = (struct ion_handle *)handle->id; if (copy_to_user((void __user *)arg, &data, sizeof(data))) { - ion_free(client, data.handle); + ion_free(client, handle); return -EFAULT; } break; @@ -1116,27 +1147,29 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case ION_IOC_FREE: { struct ion_handle_data data; - bool valid; + struct ion_handle *handle; if (copy_from_user(&data, (void __user *)arg, sizeof(struct ion_handle_data))) return -EFAULT; mutex_lock(&client->lock); - valid = ion_handle_validate(client, data.handle); + handle = ion_uhandle_get(client, (int)data.handle); mutex_unlock(&client->lock); - if (!valid) + if (!handle) return -EINVAL; - ion_free(client, data.handle); + ion_free(client, handle); break; } case ION_IOC_SHARE: case ION_IOC_MAP: { struct ion_fd_data data; + struct ion_handle *handle; if (copy_from_user(&data, (void __user *)arg, sizeof(data))) return -EFAULT; - data.fd = ion_share_dma_buf_fd(client, data.handle); + handle = ion_uhandle_get(client, (int)data.handle); + data.fd = ion_share_dma_buf_fd(client, handle); if (copy_to_user((void __user *)arg, &data, sizeof(data))) return -EFAULT; if (data.fd < 0) @@ -1146,15 +1179,17 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case ION_IOC_IMPORT: { struct ion_fd_data data; + struct ion_handle *handle; int ret = 0; if (copy_from_user(&data, (void __user *)arg, sizeof(struct ion_fd_data))) return -EFAULT; - data.handle = ion_import_dma_buf(client, data.fd); - if (IS_ERR(data.handle)) { - ret = PTR_ERR(data.handle); - data.handle = NULL; - } + handle = ion_import_dma_buf(client, data.fd); + if (IS_ERR(handle)) + ret = PTR_ERR(handle); + else + data.handle = (struct ion_handle *)handle->id; + if (copy_to_user((void __user *)arg, &data, sizeof(struct ion_fd_data))) return -EFAULT; From 002119eeb221cf1efd7afda836da768ff1cd0ad9 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 21 Aug 2013 11:23:12 -0700 Subject: [PATCH 13/49] ion: index client->handles rbtree by buffer The only remaining users of the client->handles rbtree are iterating through it like a list. Keep the rbtree, but change its index to be the buffer address instead of the handle address, which makes ion_handle_lookup a fast rbtree search. Change-Id: Ie7d974b3a5d9831c0d664de85ddae8db3c3abdf9 Signed-off-by: Colin Cross --- drivers/gpu/ion/ion.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index 8d43ad12ea6d..9f73052c3920 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -388,13 +388,16 @@ static int ion_handle_put(struct ion_handle *handle) static struct ion_handle *ion_handle_lookup(struct ion_client *client, struct ion_buffer *buffer) { - struct rb_node *n; + struct rb_node *n = client->handles.rb_node; - for (n = rb_first(&client->handles); n; n = rb_next(n)) { - struct ion_handle *handle = rb_entry(n, struct ion_handle, - node); - if (handle->buffer == buffer) - return handle; + while (n) { + struct ion_handle *entry = rb_entry(n, struct ion_handle, node); + if (buffer < entry->buffer) + n = n->rb_left; + else if (buffer > entry->buffer) + n = n->rb_right; + else + return entry; } return ERR_PTR(-EINVAL); } @@ -432,9 +435,9 @@ static int ion_handle_add(struct ion_client *client, struct ion_handle *handle) parent = *p; entry = rb_entry(parent, struct ion_handle, node); - if (handle < entry) + if (handle->buffer < entry->buffer) p = &(*p)->rb_left; - else if (handle > entry) + else if (handle->buffer > entry->buffer) p = &(*p)->rb_right; else WARN(1, "%s: buffer already found.", __func__); From 66df93b4c9e78cf433b76d7fe3da4cc7fd06c36b Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 22 Aug 2013 19:29:44 -0700 Subject: [PATCH 14/49] ion: don't use id 0 for handle cookie ion userspace clients think that the cookie is a pointer, so they use NULL to check if the handle has been initialized. Set the first id number to 1. Change-Id: Ifb9af6029a8b08f57e41bc6160cc11e11001a2a9 Signed-off-by: Colin Cross --- drivers/gpu/ion/ion.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index 9f73052c3920..1d4c8876ab5c 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -424,7 +424,7 @@ static int ion_handle_add(struct ion_client *client, struct ion_handle *handle) rc = idr_pre_get(&client->idr, GFP_KERNEL); if (!rc) return -ENOMEM; - rc = idr_get_new(&client->idr, handle, &id); + rc = idr_get_new_above(&client->idr, handle, 1, &id); handle->id = id; } while (rc == -EAGAIN); From cf0fad49d17cb8273ce555dd5b7afab67d7923bf Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Sun, 25 Aug 2013 19:23:34 +0900 Subject: [PATCH 15/49] cpufreq: interactive: fix show_target_loads and show_above_hispeed_delay Remove a trailing whitespace from target_loads and above_hispeed_delay. Problem happens when user-space program tried to restore parameters that saved before changing parameters. In this case was returned error(EINVAL). Change-Id: I5a74e3824602cd6f2b74651adda5ec1b627e61e9 Signed-off-by: Minsung Kim --- drivers/cpufreq/cpufreq_interactive.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 90958fdd64dc..a66748e13b38 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -742,7 +742,7 @@ static ssize_t show_target_loads( ret += sprintf(buf + ret, "%u%s", target_loads[i], i & 0x1 ? ":" : " "); - ret += sprintf(buf + ret, "\n"); + ret += sprintf(buf + --ret, "\n"); spin_unlock_irqrestore(&target_loads_lock, flags); return ret; } @@ -785,7 +785,7 @@ static ssize_t show_above_hispeed_delay( ret += sprintf(buf + ret, "%u%s", above_hispeed_delay[i], i & 0x1 ? ":" : " "); - ret += sprintf(buf + ret, "\n"); + ret += sprintf(buf + --ret, "\n"); spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); return ret; } From 2c7011b8323721307f74cec6eec28430cfee3e7e Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Thu, 12 Sep 2013 01:42:18 +0000 Subject: [PATCH 16/49] USB: remove duplicate out endpoint creation in MTP mode Android MTP gadget uses 3 endpoints which are 1 in endpoint, 1 out endpoint, and 1 interrupt endpoint. However when MTP gadget creates its endpoints, it creates the out endpoint twice and overwrites the first created out endpoint with the second one, so that it causes a leak of endpoint resources. Change-Id: Iba82950095610b26b362f4b10a67cedfb1fee366 Signed-off-by: Peter Oh Reviewed-on: http://mps-gerrit.broadcom.com/37744 Reviewed-by: Graham Williams Reviewed-by: John Garry Branch-Open: Branch Status Reviewed-by: Checkpatch Status Reviewed-by: Joyjit Nath Tested-by: AutoSubmit Status --- drivers/usb/gadget/f_mtp.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 9ab94697c196..12fb818ab147 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -410,15 +410,6 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev, ep->driver_data = dev; /* claim the endpoint */ dev->ep_out = ep; - ep = usb_ep_autoconfig(cdev->gadget, out_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_out = ep; - ep = usb_ep_autoconfig(cdev->gadget, intr_desc); if (!ep) { DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n"); From 79245d37165aad2e57b1919defdd124639ddf55a Mon Sep 17 00:00:00 2001 From: Bintian Wang Date: Sun, 30 Jun 2013 20:34:24 +0800 Subject: [PATCH 17/49] Add compat_ioctl support for VFAT_IOCTL_GET_VOLUME_ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add VFAT_IOCTL_GET_VOLUME_ID to vfat dir compat_ioctl() interface, which enable you read vfat volume ID from a 32bit app on a 64bit kernel Change-Id: I3e93dfcc1e7a364a6b11bc7e3f5f210e82f306ed Signed-off-by: Bintian Wang Signed-off-by: John Stultz --- fs/fat/dir.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/fat/dir.c b/fs/fat/dir.c index ddfa4529e6ea..4b775e606fc8 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -841,6 +841,8 @@ static long fat_compat_dir_ioctl(struct file *filp, unsigned cmd, short_only = 0; both = 1; break; + case VFAT_IOCTL_GET_VOLUME_ID: + return fat_ioctl_volume_id(inode); default: return fat_generic_ioctl(filp, cmd, (unsigned long)arg); } From 4fe1f96d0bc0cce09c41a6818153fea81d645a39 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Wed, 3 Jul 2013 15:01:24 -0700 Subject: [PATCH 18/49] mm: remove compressed copy from zram in-memory Swap subsystem does lazy swap slot free with expecting the page would be swapped out again so we can avoid unnecessary write. But the problem in in-memory swap(ex, zram) is that it consumes memory space until vm_swap_full(ie, used half of all of swap device) condition meet. It could be bad if we use multiple swap device, small in-memory swap and big storage swap or in-memory swap alone. This patch makes swap subsystem free swap slot as soon as swap-read is completed and make the swapcache page dirty so the page should be written out the swap device to reclaim it. It means we never lose it. I tested this patch with kernel compile workload. 1. before compile time : 9882.42 zram max wasted space by fragmentation: 13471881 byte memory space consumed by zram: 174227456 byte the number of slot free notify: 206684 2. after compile time : 9653.90 zram max wasted space by fragmentation: 11805932 byte memory space consumed by zram: 154001408 byte the number of slot free notify: 426972 [akpm@linux-foundation.org: tweak comment text] [artem.savkov@gmail.com: fix BUG due to non-swapcache pages in end_swap_bio_read()] [akpm@linux-foundation.org: invert unlikely() test, augment comment, 80-col cleanup] Signed-off-by: Dan Magenheimer Signed-off-by: Minchan Kim Signed-off-by: Artem Savkov Cc: Hugh Dickins Cc: Seth Jennings Cc: Nitin Gupta Cc: Konrad Rzeszutek Wilk Cc: Shaohua Li Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_io.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/mm/page_io.c b/mm/page_io.c index a8a3ef45fed7..ba05b64e5d8d 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -21,6 +21,7 @@ #include #include #include +#include #include static struct bio *get_swap_bio(gfp_t gfp_flags, @@ -80,9 +81,54 @@ void end_swap_bio_read(struct bio *bio, int err) imajor(bio->bi_bdev->bd_inode), iminor(bio->bi_bdev->bd_inode), (unsigned long long)bio->bi_sector); - } else { - SetPageUptodate(page); + goto out; } + + SetPageUptodate(page); + + /* + * There is no guarantee that the page is in swap cache - the software + * suspend code (at least) uses end_swap_bio_read() against a non- + * swapcache page. So we must check PG_swapcache before proceeding with + * this optimization. + */ + if (likely(PageSwapCache(page))) { + struct swap_info_struct *sis; + + sis = page_swap_info(page); + if (sis->flags & SWP_BLKDEV) { + /* + * The swap subsystem performs lazy swap slot freeing, + * expecting that the page will be swapped out again. + * So we can avoid an unnecessary write if the page + * isn't redirtied. + * This is good for real swap storage because we can + * reduce unnecessary I/O and enhance wear-leveling + * if an SSD is used as the as swap device. + * But if in-memory swap device (eg zram) is used, + * this causes a duplicated copy between uncompressed + * data in VM-owned memory and compressed data in + * zram-owned memory. So let's free zram-owned memory + * and make the VM-owned decompressed page *dirty*, + * so the page should be swapped out somewhere again if + * we again wish to reclaim it. + */ + struct gendisk *disk = sis->bdev->bd_disk; + if (disk->fops->swap_slot_free_notify) { + swp_entry_t entry; + unsigned long offset; + + entry.val = page_private(page); + offset = swp_offset(entry); + + SetPageDirty(page); + disk->fops->swap_slot_free_notify(sis->bdev, + offset); + } + } + } + +out: unlock_page(page); bio_put(bio); } From 3eeae661f1c26431dd03d2760e2373620bb488ca Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Fri, 11 Oct 2013 13:54:41 -0700 Subject: [PATCH 19/49] staging: sw_sync: add stubs for kernels without CONFIG_SW_SYNC Change-Id: I7a12679eb3c10b1beaf36c6612cf2e3f6662a034 Signed-off-by: Greg Hackmann --- drivers/staging/android/sw_sync.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/staging/android/sw_sync.h b/drivers/staging/android/sw_sync.h index 585040be5f18..aba25cbb0382 100644 --- a/drivers/staging/android/sw_sync.h +++ b/drivers/staging/android/sw_sync.h @@ -21,6 +21,7 @@ #ifdef __KERNEL__ +#include #include "sync.h" struct sw_sync_timeline { @@ -35,10 +36,27 @@ struct sw_sync_pt { u32 value; }; +#if IS_ENABLED(CONFIG_SW_SYNC) struct sw_sync_timeline *sw_sync_timeline_create(const char *name); void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc); struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value); +#else +static inline struct sw_sync_timeline *sw_sync_timeline_create(const char *name) +{ + return NULL; +} + +static inline void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) +{ +} + +static inline struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, + u32 value) +{ + return NULL; +} +#endif /* IS_ENABLED(CONFIG_SW_SYNC) */ #endif /* __KERNEL __ */ From aa431ca700139dbaaddd14e689f58625469f94d0 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Wed, 22 May 2013 14:23:10 -0700 Subject: [PATCH 20/49] video: add atomic display framework Change-Id: I693257e269a99012cd0dbb57576ac222869cf4c7 Signed-off-by: Greg Hackmann --- drivers/video/Kconfig | 1 + drivers/video/Makefile | 1 + drivers/video/adf/Kconfig | 4 + drivers/video/adf/Makefile | 11 + drivers/video/adf/adf.c | 1066 ++++++++++++++++++++++++++++++++ drivers/video/adf/adf.h | 74 +++ drivers/video/adf/adf_client.c | 764 +++++++++++++++++++++++ drivers/video/adf/adf_fops.c | 843 +++++++++++++++++++++++++ drivers/video/adf/adf_fops.h | 37 ++ drivers/video/adf/adf_fops32.c | 208 +++++++ drivers/video/adf/adf_fops32.h | 74 +++ drivers/video/adf/adf_format.c | 280 +++++++++ drivers/video/adf/adf_sysfs.c | 284 +++++++++ drivers/video/adf/adf_sysfs.h | 33 + drivers/video/adf/adf_trace.h | 93 +++ include/uapi/video/adf.h | 254 ++++++++ include/video/adf.h | 439 +++++++++++++ include/video/adf_client.h | 57 ++ include/video/adf_format.h | 26 + 19 files changed, 4549 insertions(+) create mode 100644 drivers/video/adf/Kconfig create mode 100644 drivers/video/adf/Makefile create mode 100644 drivers/video/adf/adf.c create mode 100644 drivers/video/adf/adf.h create mode 100644 drivers/video/adf/adf_client.c create mode 100644 drivers/video/adf/adf_fops.c create mode 100644 drivers/video/adf/adf_fops.h create mode 100644 drivers/video/adf/adf_fops32.c create mode 100644 drivers/video/adf/adf_fops32.h create mode 100644 drivers/video/adf/adf_format.c create mode 100644 drivers/video/adf/adf_sysfs.c create mode 100644 drivers/video/adf/adf_sysfs.h create mode 100644 drivers/video/adf/adf_trace.h create mode 100644 include/uapi/video/adf.h create mode 100644 include/video/adf.h create mode 100644 include/video/adf_client.h create mode 100644 include/video/adf_format.h diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index da4766b456f0..7c5e27e52b9f 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2477,6 +2477,7 @@ source "drivers/video/omap2/Kconfig" source "drivers/video/exynos/Kconfig" source "drivers/video/mmp/Kconfig" source "drivers/video/backlight/Kconfig" +source "drivers/video/adf/Kconfig" if VT source "drivers/video/console/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index e8bae8dd4804..2babdef5fe0b 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -12,6 +12,7 @@ fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ modedb.o fbcvt.o fb-objs := $(fb-y) +obj-$(CONFIG_ADF) += adf/ obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += backlight/ diff --git a/drivers/video/adf/Kconfig b/drivers/video/adf/Kconfig new file mode 100644 index 000000000000..0131dfb940d5 --- /dev/null +++ b/drivers/video/adf/Kconfig @@ -0,0 +1,4 @@ +menuconfig ADF + depends on SYNC + depends on DMA_SHARED_BUFFER + tristate "Atomic Display Framework" diff --git a/drivers/video/adf/Makefile b/drivers/video/adf/Makefile new file mode 100644 index 000000000000..62679a0da604 --- /dev/null +++ b/drivers/video/adf/Makefile @@ -0,0 +1,11 @@ +ccflags-y := -Idrivers/staging/android + +CFLAGS_adf.o := -I$(src) + +obj-$(CONFIG_ADF) += adf.o \ + adf_client.o \ + adf_fops.o \ + adf_format.o \ + adf_sysfs.o + +obj-$(CONFIG_COMPAT) += adf_fops32.o diff --git a/drivers/video/adf/adf.c b/drivers/video/adf/adf.c new file mode 100644 index 000000000000..4e359602748f --- /dev/null +++ b/drivers/video/adf/adf.c @@ -0,0 +1,1066 @@ +/* + * Copyright (C) 2013 Google, Inc. + * adf_modeinfo_{set_name,set_vrefresh} modified from + * drivers/gpu/drm/drm_modes.c + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include