kernel_heap.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*!
  2. *****************************************************************************
  3. *
  4. * @File kernel_heap.c
  5. * @Description MMU Library: device virtual allocation (heap) implementation
  6. * using gen_alloc from the Linux kernel
  7. * ---------------------------------------------------------------------------
  8. *
  9. * Copyright (c) Imagination Technologies Ltd.
  10. *
  11. * The contents of this file are subject to the MIT license as set out below.
  12. *
  13. * Permission is hereby granted, free of charge, to any person obtaining a
  14. * copy of this software and associated documentation files (the "Software"),
  15. * to deal in the Software without restriction, including without limitation
  16. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  17. * and/or sell copies of the Software, and to permit persons to whom the
  18. * Software is furnished to do so, subject to the following conditions:
  19. *
  20. * The above copyright notice and this permission notice shall be included in
  21. * all copies or substantial portions of the Software.
  22. *
  23. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  24. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  25. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  26. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  27. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  28. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  29. * THE SOFTWARE.
  30. *
  31. * Alternatively, the contents of this file may be used under the terms of the
  32. * GNU General Public License Version 2 ("GPL")in which case the provisions of
  33. * GPL are applicable instead of those above.
  34. *
  35. * If you wish to allow use of your version of this file only under the terms
  36. * of GPL, and not to allow others to use your version of this file under the
  37. * terms of the MIT license, indicate your decision by deleting the provisions
  38. * above and replace them with the notice and other provisions required by GPL
  39. * as set out in the file called "GPLHEADER" included in this distribution. If
  40. * you do not delete the provisions above, a recipient may use your version of
  41. * this file under the terms of either the MIT license or GPL.
  42. *
  43. * This License is also included in this distribution in the file called
  44. * "MIT_COPYING".
  45. *
  46. *****************************************************************************/
  47. #include <linux/module.h>
  48. #include <linux/version.h>
  49. #include <linux/init.h>
  50. #include <linux/genalloc.h>
  51. #include <linux/slab.h>
  52. #include "mmulib/heap.h"
  53. /* access to MMU info and error printing function */
  54. #include "mmu_defs.h"
  55. /*#define DEBUG_POOL 1 */
  56. /*
  57. * Internal heap object using genalloc
  58. */
  59. struct gen_heap {
  60. struct gen_pool *pool;
  61. size_t nalloc;
  62. struct imgmmu_heap hinfo;
  63. };
  64. /*
  65. * The Heap allocation - contains an imgmmu_halloc
  66. * that is given to the caller
  67. */
  68. struct gen_halloc {
  69. /*
  70. * Associated heap
  71. */
  72. struct gen_heap *heap;
  73. /*
  74. * MMU lib allocation part
  75. */
  76. struct imgmmu_halloc virt_mem;
  77. };
  78. /*
  79. * be used for debugging
  80. */
  81. static void pool_crawler(struct gen_pool *pool,
  82. struct gen_pool_chunk *chunk, void *data) __maybe_unused;
  83. static void pool_crawler(struct gen_pool *pool,
  84. struct gen_pool_chunk *chunk, void *data)
  85. {
  86. #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0)
  87. unsigned long size = (chunk->end_addr - chunk->start_addr);
  88. #else
  89. unsigned long size = (chunk->end_addr - chunk->start_addr + 1);
  90. #endif
  91. pr_info("pool 0x%p has chunk 0x%lx to 0x%lx (size = %lu B)\n",
  92. data, chunk->start_addr, chunk->end_addr, size);
  93. }
  94. struct imgmmu_heap *imgmmu_hcreate(uintptr_t vaddr_start,
  95. size_t atom, size_t size, bool guard_band, int *res)
  96. {
  97. struct gen_heap *iheap = NULL;
  98. int min_order = 0; /* log2 of the alloc atom */
  99. size_t isize = atom;
  100. int ret;
  101. uintptr_t start = vaddr_start;
  102. WARN_ON(res == NULL);
  103. WARN_ON(size == 0);
  104. if (size%atom != 0 || (vaddr_start != 0 && vaddr_start%atom != 0)) {
  105. mmu_log_err("Wrong input params: %zu %zu %zu %zu %zu %zu\n",
  106. size, atom, size%atom,
  107. vaddr_start, atom, vaddr_start%atom);
  108. *res = -EINVAL;
  109. return NULL;
  110. }
  111. iheap = kzalloc(sizeof(struct gen_heap), GFP_KERNEL);
  112. if (iheap == NULL) {
  113. *res = -ENOMEM;
  114. return NULL;
  115. }
  116. iheap->nalloc = 0;
  117. /* compute log2 of the alloc atom */
  118. while (isize >>= 1)
  119. min_order++;
  120. /* ugly fix for trouble using gen_pool_alloc() when allocating a block
  121. * gen_pool_alloc() returns 0 on error alought 0 can be a valid
  122. * first virtual address
  123. * therefore all addresses are offseted by the allocation atom
  124. * to insure 0 is the actual error code
  125. */
  126. if (vaddr_start == 0)
  127. start = vaddr_start+atom; /* otherwise it is vaddr_start */
  128. #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0)
  129. isize = start + size;
  130. #else
  131. isize = start + size - 1;
  132. #endif
  133. WARN_ON(isize < start); /* too big! it did an overflow */
  134. mmu_log_dbg("create genalloc pool of order %u\n", min_order);
  135. /* -1: not using real inode */
  136. iheap->pool = gen_pool_create(min_order, -1);
  137. if (iheap->pool == NULL) {
  138. *res = -ENOMEM;
  139. mmu_log_err("Failure to create the genalloc pool\n");
  140. kfree(iheap);
  141. return NULL;
  142. }
  143. mmu_log_dbg("pool 0x%p order %u region from 0x%x for %zu bytes\n",
  144. iheap->pool, min_order, start, size);
  145. ret = gen_pool_add(iheap->pool, start, size, -1);
  146. if (ret != 0) {
  147. *res = -EFAULT;
  148. mmu_log_err("Failure to configure the new pool: %d\n", ret);
  149. gen_pool_destroy(iheap->pool);
  150. kfree(iheap);
  151. return NULL;
  152. }
  153. #ifdef DEBUG_POOL
  154. gen_pool_for_each_chunk(iheap->pool, &pool_crawler, iheap->pool);
  155. #endif
  156. iheap->hinfo.vaddr_start = vaddr_start;
  157. iheap->hinfo.atom = atom;
  158. iheap->hinfo.size = size;
  159. iheap->hinfo.guard_band = guard_band;
  160. *res = 0;
  161. return &(iheap->hinfo);
  162. }
  163. struct imgmmu_halloc *imgmmu_hallocate(struct imgmmu_heap *heap,
  164. size_t size, int *res)
  165. {
  166. struct gen_heap *iheap = NULL;
  167. struct gen_halloc *ialloc = NULL;
  168. WARN_ON(res == NULL);
  169. WARN_ON(heap == NULL);
  170. iheap = container_of(heap, struct gen_heap, hinfo);
  171. if (size%heap->atom != 0 || size == 0) {
  172. mmu_log_err("invalid alloc size (0x%zx) for atom:%zu\n",
  173. size, heap->atom);
  174. *res = -EINVAL;
  175. return NULL;
  176. }
  177. ialloc = kzalloc(sizeof(struct gen_halloc), GFP_KERNEL);
  178. if (ialloc == NULL) {
  179. mmu_log_err("failed to allocate internal structure\n");
  180. *res = -ENOMEM;
  181. return NULL;
  182. }
  183. mmu_log_dbg("heap 0x%p alloc %u\n", iheap->pool, size);
  184. /* gen_pool_alloc returns 0 on error
  185. * that is a problem when 1st valid address is 0
  186. * check imgmmu_hcreate for explanations
  187. */
  188. ialloc->virt_mem.vaddr = gen_pool_alloc(iheap->pool,
  189. /* Take one more atom to create a fake gap between
  190. * virtual addresses, when needed */
  191. iheap->hinfo.guard_band ?
  192. size + iheap->hinfo.atom :
  193. size);
  194. if (ialloc->virt_mem.vaddr == 0) {
  195. mmu_log_err("failed to allocate from gen_pool_alloc\n");
  196. *res = -EFAULT;
  197. kfree(ialloc);
  198. return NULL;
  199. }
  200. mmu_log_dbg(KERN_INFO "heap 0x%p alloc 0x%p %u B atom %u B\n",
  201. iheap->pool, ialloc->virt_mem.vaddr, size, iheap->hinfo.atom);
  202. /* if base address is 0 we applied an offset */
  203. if (iheap->hinfo.vaddr_start == 0)
  204. ialloc->virt_mem.vaddr -= iheap->hinfo.atom;
  205. ialloc->virt_mem.size = size;
  206. ialloc->heap = iheap;
  207. iheap->nalloc++;
  208. #ifdef DEBUG_POOL
  209. gen_pool_for_each_chunk(iheap->pool, &pool_crawler, iheap->pool);
  210. #endif
  211. *res = 0;
  212. return &(ialloc->virt_mem);
  213. }
  214. int imgmmu_hfree(struct imgmmu_halloc *alloc)
  215. {
  216. struct gen_halloc *ialloc = NULL;
  217. uintptr_t addr = 0;
  218. size_t size;
  219. WARN_ON(alloc == NULL);
  220. ialloc = container_of(alloc, struct gen_halloc, virt_mem);
  221. WARN_ON(ialloc->heap == NULL);
  222. WARN_ON(ialloc->heap->pool == NULL);
  223. WARN_ON(ialloc->heap->nalloc == 0);
  224. mmu_log_dbg("heap 0x%p free 0x%p %u B\n",
  225. ialloc->heap->pool, alloc->vaddr, alloc->size);
  226. #ifdef DEBUG_POOL
  227. gen_pool_for_each_chunk(ialloc->heap->pool,
  228. &pool_crawler, ialloc->heap->pool);
  229. #endif
  230. addr = alloc->vaddr;
  231. /* Include a fake gap */
  232. size = ialloc->heap->hinfo.guard_band ?
  233. alloc->size + ialloc->heap->hinfo.atom :
  234. alloc->size;
  235. /* see the explanation in imgmmu_hcreate to know why + atom */
  236. if (ialloc->heap->hinfo.vaddr_start == 0)
  237. addr += ialloc->heap->hinfo.atom;
  238. gen_pool_free(ialloc->heap->pool, addr, size);
  239. ialloc->heap->nalloc--;
  240. kfree(ialloc);
  241. return 0;
  242. }
  243. int imgmmu_hdestroy(struct imgmmu_heap *heap)
  244. {
  245. struct gen_heap *iheap = NULL;
  246. WARN_ON(heap == NULL);
  247. iheap = container_of(heap, struct gen_heap, hinfo);
  248. if (iheap->nalloc > 0) {
  249. mmu_log_err("destroying a heap with non-freed allocation\n");
  250. return -EFAULT;
  251. }
  252. if (iheap->pool != NULL) {
  253. mmu_log_dbg("destroying genalloc pool 0x%p\n", iheap->pool);
  254. gen_pool_destroy(iheap->pool);
  255. }
  256. kfree(iheap);
  257. return 0;
  258. }