Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ion: carveout heap: zero buffers on free, fix memory leak

The carveout heap wasn't zeroing its buffers after use.
Create the sg_table during allocate instead of map_dma, to allow
using the sg_table during free, and call ion_heap_buffer_zero
during free. Also fixes a missing kfree when destroying the
table.

Signed-off-by: Colin Cross <ccross@android.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Colin Cross and committed by
Greg Kroah-Hartman
ed5bf01a f63958d8

+48 -21
+48 -21
drivers/staging/android/ion/ion_carveout_heap.c
··· 14 14 * 15 15 */ 16 16 #include <linux/spinlock.h> 17 - 17 + #include <linux/dma-mapping.h> 18 18 #include <linux/err.h> 19 19 #include <linux/genalloc.h> 20 20 #include <linux/io.h> ··· 60 60 struct ion_buffer *buffer, 61 61 ion_phys_addr_t *addr, size_t *len) 62 62 { 63 - *addr = buffer->priv_phys; 63 + struct sg_table *table = buffer->priv_virt; 64 + struct page *page = sg_page(table->sgl); 65 + ion_phys_addr_t paddr = PFN_PHYS(page_to_pfn(page)); 66 + 67 + *addr = paddr; 64 68 *len = buffer->size; 65 69 return 0; 66 70 } ··· 74 70 unsigned long size, unsigned long align, 75 71 unsigned long flags) 76 72 { 73 + struct sg_table *table; 74 + ion_phys_addr_t paddr; 75 + int ret; 76 + 77 77 if (align > PAGE_SIZE) 78 78 return -EINVAL; 79 79 80 - buffer->priv_phys = ion_carveout_allocate(heap, size, align); 81 - return buffer->priv_phys == ION_CARVEOUT_ALLOCATE_FAIL ? -ENOMEM : 0; 80 + table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); 81 + if (!table) 82 + return -ENOMEM; 83 + ret = sg_alloc_table(table, 1, GFP_KERNEL); 84 + if (ret) 85 + goto err_free; 86 + 87 + paddr = ion_carveout_allocate(heap, size, align); 88 + if (paddr == ION_CARVEOUT_ALLOCATE_FAIL) { 89 + ret = -ENOMEM; 90 + goto err_free_table; 91 + } 92 + 93 + sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(paddr)), size, 0); 94 + buffer->priv_virt = table; 95 + 96 + return 0; 97 + 98 + err_free_table: 99 + sg_free_table(table); 100 + err_free: 101 + kfree(table); 102 + return ret; 82 103 } 83 104 84 105 static void ion_carveout_heap_free(struct ion_buffer *buffer) 85 106 { 86 107 struct ion_heap *heap = buffer->heap; 108 + struct sg_table *table = buffer->priv_virt; 109 + struct page *page = sg_page(table->sgl); 110 + ion_phys_addr_t paddr = PFN_PHYS(page_to_pfn(page)); 87 111 88 - ion_carveout_free(heap, buffer->priv_phys, buffer->size); 89 - buffer->priv_phys = ION_CARVEOUT_ALLOCATE_FAIL; 112 + ion_heap_buffer_zero(buffer); 113 + 114 + if (ion_buffer_cached(buffer)) 115 + dma_sync_sg_for_device(NULL, table->sgl, table->nents, 116 + DMA_BIDIRECTIONAL); 117 + 118 + ion_carveout_free(heap, paddr, buffer->size); 119 + sg_free_table(table); 120 + kfree(table); 90 121 } 91 122 92 123 static struct sg_table *ion_carveout_heap_map_dma(struct ion_heap *heap, 93 124 struct ion_buffer *buffer) 94 125 { 95 - struct sg_table *table; 96 - int ret; 97 - 98 - table = kzalloc(sizeof(struct sg_table), GFP_KERNEL); 99 - if (!table) 100 - return ERR_PTR(-ENOMEM); 101 - ret = sg_alloc_table(table, 1, GFP_KERNEL); 102 - if (ret) { 103 - kfree(table); 104 - return ERR_PTR(ret); 105 - } 106 - sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(buffer->priv_phys)), 107 - buffer->size, 0); 108 - return table; 126 + return buffer->priv_virt; 109 127 } 110 128 111 129 static void ion_carveout_heap_unmap_dma(struct ion_heap *heap, 112 130 struct ion_buffer *buffer) 113 131 { 114 - sg_free_table(buffer->sg_table); 132 + return; 115 133 } 116 134 117 135 static struct ion_heap_ops carveout_heap_ops = { ··· 165 139 -1); 166 140 carveout_heap->heap.ops = &carveout_heap_ops; 167 141 carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT; 142 + carveout_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE; 168 143 169 144 return &carveout_heap->heap; 170 145 }