Files
thead-kernel/drivers/nna/img_mem/imgmmu/kernel_heap.c
Mingzheng Xing 9e49618e75 driver: add npu-ax3386-gpl
Add a new driver npu-ax3386-gpl, default compile as module.
The default generated modules are:
- vha.ko
- img_mem.ko
- vha_info.ko

Signed-off-by: Mingzheng Xing <xingmingzheng@iscas.ac.cn>
2023-07-19 02:15:19 +08:00

308 lines
8.6 KiB
C

/*!
*****************************************************************************
*
* @File kernel_heap.c
* @Description MMU Library: device virtual allocation (heap) implementation
* using gen_alloc from the Linux kernel
* ---------------------------------------------------------------------------
*
* Copyright (c) Imagination Technologies Ltd.
*
* The contents of this file are subject to the MIT license as set out below.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the terms of the
* GNU General Public License Version 2 ("GPL")in which case the provisions of
* GPL are applicable instead of those above.
*
* If you wish to allow use of your version of this file only under the terms
* of GPL, and not to allow others to use your version of this file under the
* terms of the MIT license, indicate your decision by deleting the provisions
* above and replace them with the notice and other provisions required by GPL
* as set out in the file called "GPLHEADER" included in this distribution. If
* you do not delete the provisions above, a recipient may use your version of
* this file under the terms of either the MIT license or GPL.
*
* This License is also included in this distribution in the file called
* "MIT_COPYING".
*
*****************************************************************************/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/genalloc.h>
#include <linux/slab.h>
#include "mmulib/heap.h"
/* access to MMU info and error printing function */
#include "mmu_defs.h"
/*#define DEBUG_POOL 1 */
/*
* Internal heap object using genalloc
*/
struct gen_heap {
struct gen_pool *pool;
size_t nalloc;
struct imgmmu_heap hinfo;
};
/*
* The Heap allocation - contains an imgmmu_halloc
* that is given to the caller
*/
struct gen_halloc {
/*
* Associated heap
*/
struct gen_heap *heap;
/*
* MMU lib allocation part
*/
struct imgmmu_halloc virt_mem;
};
/*
* be used for debugging
*/
static void pool_crawler(struct gen_pool *pool,
struct gen_pool_chunk *chunk, void *data) __maybe_unused;
static void pool_crawler(struct gen_pool *pool,
struct gen_pool_chunk *chunk, void *data)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0)
unsigned long size = (chunk->end_addr - chunk->start_addr);
#else
unsigned long size = (chunk->end_addr - chunk->start_addr + 1);
#endif
pr_debug("pool 0x%p has chunk 0x%lx to 0x%lx (size = %lu B)\n",
data, chunk->start_addr, chunk->end_addr, size);
}
struct imgmmu_heap *imgmmu_hcreate(uintptr_t vaddr_start,
size_t atom, size_t size, bool guard_band, int *res)
{
struct gen_heap *iheap = NULL;
int min_order = 0; /* log2 of the alloc atom */
size_t isize = atom;
int ret;
uintptr_t start = vaddr_start;
WARN_ON(res == NULL);
WARN_ON(size == 0);
if (size%atom != 0 || (vaddr_start != 0 && vaddr_start%atom != 0)) {
mmu_log_err("Wrong input params: %zu %zu %zu %zu %zu %zu\n",
size, atom, size%atom,
vaddr_start, atom, vaddr_start%atom);
*res = -EINVAL;
return NULL;
}
iheap = kzalloc(sizeof(struct gen_heap), GFP_KERNEL);
if (iheap == NULL) {
*res = -ENOMEM;
return NULL;
}
iheap->nalloc = 0;
/* compute log2 of the alloc atom */
while (isize >>= 1)
min_order++;
/* ugly fix for trouble using gen_pool_alloc() when allocating a block
* gen_pool_alloc() returns 0 on error alought 0 can be a valid
* first virtual address
* therefore all addresses are offseted by the allocation atom
* to insure 0 is the actual error code
*/
if (vaddr_start == 0)
start = vaddr_start+atom; /* otherwise it is vaddr_start */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0)
isize = start + size;
#else
isize = start + size - 1;
#endif
WARN_ON(isize < start); /* too big! it did an overflow */
mmu_log_dbg("create genalloc pool of order %u\n", min_order);
/* -1: not using real inode */
iheap->pool = gen_pool_create(min_order, -1);
if (iheap->pool == NULL) {
*res = -ENOMEM;
mmu_log_err("Failure to create the genalloc pool\n");
kfree(iheap);
return NULL;
}
mmu_log_dbg("pool 0x%p order %u region from 0x%x for %zu bytes\n",
iheap->pool, min_order, start, size);
ret = gen_pool_add(iheap->pool, start, size, -1);
if (ret != 0) {
*res = -EFAULT;
mmu_log_err("Failure to configure the new pool: %d\n", ret);
gen_pool_destroy(iheap->pool);
kfree(iheap);
return NULL;
}
#ifdef DEBUG_POOL
gen_pool_for_each_chunk(iheap->pool, &pool_crawler, iheap->pool);
#endif
iheap->hinfo.vaddr_start = vaddr_start;
iheap->hinfo.atom = atom;
iheap->hinfo.size = size;
iheap->hinfo.guard_band = guard_band;
*res = 0;
return &(iheap->hinfo);
}
struct imgmmu_halloc *imgmmu_hallocate(struct imgmmu_heap *heap,
size_t size, int *res)
{
struct gen_heap *iheap = NULL;
struct gen_halloc *ialloc = NULL;
WARN_ON(res == NULL);
WARN_ON(heap == NULL);
iheap = container_of(heap, struct gen_heap, hinfo);
if (size%heap->atom != 0 || size == 0) {
mmu_log_err("invalid alloc size (0x%zx) for atom:%zu\n",
size, heap->atom);
*res = -EINVAL;
return NULL;
}
ialloc = kzalloc(sizeof(struct gen_halloc), GFP_KERNEL);
if (ialloc == NULL) {
mmu_log_err("failed to allocate internal structure\n");
*res = -ENOMEM;
return NULL;
}
mmu_log_dbg("heap 0x%p alloc %u\n", iheap->pool, size);
/* gen_pool_alloc returns 0 on error
* that is a problem when 1st valid address is 0
* check imgmmu_hcreate for explanations
*/
ialloc->virt_mem.vaddr = gen_pool_alloc(iheap->pool,
/* Take one more atom to create a fake gap between
* virtual addresses, when needed */
iheap->hinfo.guard_band ?
size + iheap->hinfo.atom :
size);
if (ialloc->virt_mem.vaddr == 0) {
mmu_log_err("failed to allocate from gen_pool_alloc\n");
*res = -EFAULT;
kfree(ialloc);
return NULL;
}
mmu_log_dbg(KERN_INFO "heap 0x%p alloc 0x%p %u B atom %u B\n",
iheap->pool, ialloc->virt_mem.vaddr, size, iheap->hinfo.atom);
/* if base address is 0 we applied an offset */
if (iheap->hinfo.vaddr_start == 0)
ialloc->virt_mem.vaddr -= iheap->hinfo.atom;
ialloc->virt_mem.size = size;
ialloc->heap = iheap;
iheap->nalloc++;
#ifdef DEBUG_POOL
gen_pool_for_each_chunk(iheap->pool, &pool_crawler, iheap->pool);
#endif
*res = 0;
return &(ialloc->virt_mem);
}
int imgmmu_hfree(struct imgmmu_halloc *alloc)
{
struct gen_halloc *ialloc = NULL;
uintptr_t addr = 0;
size_t size;
WARN_ON(alloc == NULL);
ialloc = container_of(alloc, struct gen_halloc, virt_mem);
WARN_ON(ialloc->heap == NULL);
WARN_ON(ialloc->heap->pool == NULL);
WARN_ON(ialloc->heap->nalloc == 0);
mmu_log_dbg("heap 0x%p free 0x%p %u B\n",
ialloc->heap->pool, alloc->vaddr, alloc->size);
#ifdef DEBUG_POOL
gen_pool_for_each_chunk(ialloc->heap->pool,
&pool_crawler, ialloc->heap->pool);
#endif
addr = alloc->vaddr;
/* Include a fake gap */
size = ialloc->heap->hinfo.guard_band ?
alloc->size + ialloc->heap->hinfo.atom :
alloc->size;
/* see the explanation in imgmmu_hcreate to know why + atom */
if (ialloc->heap->hinfo.vaddr_start == 0)
addr += ialloc->heap->hinfo.atom;
gen_pool_free(ialloc->heap->pool, addr, size);
ialloc->heap->nalloc--;
kfree(ialloc);
return 0;
}
int imgmmu_hdestroy(struct imgmmu_heap *heap)
{
struct gen_heap *iheap = NULL;
WARN_ON(heap == NULL);
iheap = container_of(heap, struct gen_heap, hinfo);
if (iheap->nalloc > 0) {
mmu_log_err("destroying a heap with non-freed allocation\n");
return -EFAULT;
}
if (iheap->pool != NULL) {
mmu_log_dbg("destroying genalloc pool 0x%p\n", iheap->pool);
gen_pool_destroy(iheap->pool);
}
kfree(iheap);
return 0;
}