317 lines
8.6 KiB
C
317 lines
8.6 KiB
C
/*
|
|
* mem_manager.c
|
|
*
|
|
* memory manager
|
|
*
|
|
* Copyright (C) 2014-2018 NVIDIA Corporation. All rights reserved.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "%s : %d, " fmt, __func__, __LINE__
|
|
|
|
#include <linux/list.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/err.h>
|
|
#include <linux/seq_file.h>
|
|
|
|
#include "mem_manager.h"
|
|
|
|
static void clear_alloc_list(struct mem_manager_info *mm_info);
|
|
|
|
void *mem_request(void *mem_handle, const char *name, size_t size)
|
|
{
|
|
unsigned long flags;
|
|
struct mem_manager_info *mm_info =
|
|
(struct mem_manager_info *)mem_handle;
|
|
struct mem_chunk *mc_iterator = NULL, *best_match_chunk = NULL;
|
|
struct mem_chunk *new_mc = NULL;
|
|
|
|
spin_lock_irqsave(&mm_info->lock, flags);
|
|
|
|
/* Is mem full? */
|
|
if (list_empty(mm_info->free_list)) {
|
|
pr_err("%s : memory full\n", mm_info->name);
|
|
spin_unlock_irqrestore(&mm_info->lock, flags);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
/* Find the best size match */
|
|
list_for_each_entry(mc_iterator, mm_info->free_list, node) {
|
|
if (mc_iterator->size >= size) {
|
|
if (best_match_chunk == NULL)
|
|
best_match_chunk = mc_iterator;
|
|
else if (mc_iterator->size < best_match_chunk->size)
|
|
best_match_chunk = mc_iterator;
|
|
}
|
|
}
|
|
|
|
/* Is free node found? */
|
|
if (best_match_chunk == NULL) {
|
|
pr_err("%s : no enough memory available\n", mm_info->name);
|
|
spin_unlock_irqrestore(&mm_info->lock, flags);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
/* Is it exact match? */
|
|
if (best_match_chunk->size == size) {
|
|
list_del(&best_match_chunk->node);
|
|
list_for_each_entry(mc_iterator, mm_info->alloc_list, node) {
|
|
if (best_match_chunk->address < mc_iterator->address) {
|
|
list_add_tail(&best_match_chunk->node,
|
|
&mc_iterator->node);
|
|
strlcpy(best_match_chunk->name, name,
|
|
NAME_SIZE);
|
|
spin_unlock_irqrestore(&mm_info->lock, flags);
|
|
return best_match_chunk;
|
|
}
|
|
}
|
|
list_add(&best_match_chunk->node, mm_info->alloc_list);
|
|
strlcpy(best_match_chunk->name, name, NAME_SIZE);
|
|
spin_unlock_irqrestore(&mm_info->lock, flags);
|
|
return best_match_chunk;
|
|
} else {
|
|
new_mc = kzalloc(sizeof(struct mem_chunk), GFP_ATOMIC);
|
|
if (unlikely(!new_mc)) {
|
|
pr_err("failed to allocate memory for mem_chunk\n");
|
|
|
|
spin_unlock_irqrestore(&mm_info->lock, flags);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
new_mc->address = best_match_chunk->address;
|
|
new_mc->size = size;
|
|
strlcpy(new_mc->name, name, NAME_SIZE);
|
|
best_match_chunk->address += size;
|
|
best_match_chunk->size -= size;
|
|
list_for_each_entry(mc_iterator, mm_info->alloc_list, node) {
|
|
if (new_mc->address < mc_iterator->address) {
|
|
list_add_tail(&new_mc->node,
|
|
&mc_iterator->node);
|
|
spin_unlock_irqrestore(&mm_info->lock, flags);
|
|
return new_mc;
|
|
}
|
|
}
|
|
list_add_tail(&new_mc->node, mm_info->alloc_list);
|
|
spin_unlock_irqrestore(&mm_info->lock, flags);
|
|
return new_mc;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find the node with sepcified address and remove it from list
|
|
*/
|
|
bool mem_release(void *mem_handle, void *handle)
|
|
{
|
|
unsigned long flags;
|
|
struct mem_manager_info *mm_info =
|
|
(struct mem_manager_info *)mem_handle;
|
|
struct mem_chunk *mc_curr = NULL, *mc_prev = NULL;
|
|
struct mem_chunk *mc_free = (struct mem_chunk *)handle;
|
|
|
|
pr_debug(" addr = %lu, size = %lu, name = %s\n",
|
|
mc_free->address, mc_free->size, mc_free->name);
|
|
|
|
spin_lock_irqsave(&mm_info->lock, flags);
|
|
|
|
list_for_each_entry(mc_curr, mm_info->free_list, node) {
|
|
if (mc_free->address < mc_curr->address) {
|
|
|
|
strlcpy(mc_free->name, "FREE", NAME_SIZE);
|
|
|
|
/* adjacent next free node */
|
|
if (mc_curr->address ==
|
|
(mc_free->address + mc_free->size)) {
|
|
|
|
mc_curr->address = mc_free->address;
|
|
mc_curr->size += mc_free->size;
|
|
list_del(&mc_free->node);
|
|
kfree(mc_free);
|
|
|
|
/* and adjacent prev free node */
|
|
if ((mc_prev != NULL) &&
|
|
((mc_prev->address + mc_prev->size) ==
|
|
mc_curr->address)) {
|
|
|
|
mc_prev->size += mc_curr->size;
|
|
list_del(&mc_curr->node);
|
|
kfree(mc_curr);
|
|
}
|
|
}
|
|
/* adjacent prev free node */
|
|
else if ((mc_prev != NULL) &&
|
|
((mc_prev->address + mc_prev->size) ==
|
|
mc_free->address)) {
|
|
|
|
mc_prev->size += mc_free->size;
|
|
list_del(&mc_free->node);
|
|
kfree(mc_free);
|
|
} else {
|
|
list_del(&mc_free->node);
|
|
list_add_tail(&mc_free->node,
|
|
&mc_curr->node);
|
|
}
|
|
spin_unlock_irqrestore(&mm_info->lock, flags);
|
|
return true;
|
|
}
|
|
mc_prev = mc_curr;
|
|
}
|
|
spin_unlock_irqrestore(&mm_info->lock, flags);
|
|
return false;
|
|
}
|
|
|
|
inline unsigned long mem_get_address(void *handle)
|
|
{
|
|
struct mem_chunk *mc = (struct mem_chunk *)handle;
|
|
return mc->address;
|
|
}
|
|
|
|
void mem_print(void *mem_handle)
|
|
{
|
|
struct mem_manager_info *mm_info =
|
|
(struct mem_manager_info *)mem_handle;
|
|
struct mem_chunk *mc_iterator = NULL;
|
|
|
|
pr_info("------------------------------------\n");
|
|
pr_info("%s ALLOCATED\n", mm_info->name);
|
|
list_for_each_entry(mc_iterator, mm_info->alloc_list, node) {
|
|
pr_info(" addr = %lu, size = %lu, name = %s\n",
|
|
mc_iterator->address, mc_iterator->size,
|
|
mc_iterator->name);
|
|
}
|
|
|
|
pr_info("%s FREE\n", mm_info->name);
|
|
list_for_each_entry(mc_iterator, mm_info->free_list, node) {
|
|
pr_info(" addr = %lu, size = %lu, name = %s\n",
|
|
mc_iterator->address, mc_iterator->size,
|
|
mc_iterator->name);
|
|
}
|
|
|
|
pr_info("------------------------------------\n");
|
|
}
|
|
|
|
void mem_dump(void *mem_handle, struct seq_file *s)
|
|
{
|
|
struct mem_manager_info *mm_info =
|
|
(struct mem_manager_info *)mem_handle;
|
|
struct mem_chunk *mc_iterator = NULL;
|
|
|
|
seq_puts(s, "---------------------------------------\n");
|
|
seq_printf(s, "%s ALLOCATED\n", mm_info->name);
|
|
list_for_each_entry(mc_iterator, mm_info->alloc_list, node) {
|
|
seq_printf(s, " addr = %lu, size = %lu, name = %s\n",
|
|
mc_iterator->address, mc_iterator->size,
|
|
mc_iterator->name);
|
|
}
|
|
|
|
seq_printf(s, "%s FREE\n", mm_info->name);
|
|
list_for_each_entry(mc_iterator, mm_info->free_list, node) {
|
|
seq_printf(s, " addr = %lu, size = %lu, name = %s\n",
|
|
mc_iterator->address, mc_iterator->size,
|
|
mc_iterator->name);
|
|
}
|
|
|
|
seq_puts(s, "---------------------------------------\n");
|
|
}
|
|
|
|
static void clear_alloc_list(struct mem_manager_info *mm_info)
|
|
{
|
|
struct list_head *curr, *next;
|
|
struct mem_chunk *mc = NULL;
|
|
|
|
list_for_each_safe(curr, next, mm_info->alloc_list) {
|
|
mc = list_entry(curr, struct mem_chunk, node);
|
|
pr_debug(" addr = %lu, size = %lu, name = %s\n",
|
|
mc->address, mc->size,
|
|
mc->name);
|
|
mem_release(mm_info, mc);
|
|
}
|
|
}
|
|
|
|
void *create_mem_manager(const char *name, unsigned long start_address,
|
|
unsigned long size)
|
|
{
|
|
void *ret = NULL;
|
|
struct mem_chunk *mc;
|
|
struct mem_manager_info *mm_info =
|
|
kzalloc(sizeof(struct mem_manager_info), GFP_KERNEL);
|
|
if (unlikely(!mm_info)) {
|
|
pr_err("failed to allocate memory for mem_manager_info\n");
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
strlcpy(mm_info->name, name, NAME_SIZE);
|
|
|
|
mm_info->alloc_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
|
|
if (unlikely(!mm_info->alloc_list)) {
|
|
pr_err("failed to allocate memory for alloc_list\n");
|
|
ret = ERR_PTR(-ENOMEM);
|
|
goto free_mm_info;
|
|
}
|
|
|
|
mm_info->free_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
|
|
if (unlikely(!mm_info->free_list)) {
|
|
pr_err("failed to allocate memory for free_list\n");
|
|
ret = ERR_PTR(-ENOMEM);
|
|
goto free_alloc_list;
|
|
}
|
|
|
|
INIT_LIST_HEAD(mm_info->alloc_list);
|
|
INIT_LIST_HEAD(mm_info->free_list);
|
|
|
|
mm_info->start_address = start_address;
|
|
mm_info->size = size;
|
|
|
|
/* Add whole memory to free list */
|
|
mc = kzalloc(sizeof(struct mem_chunk), GFP_KERNEL);
|
|
if (unlikely(!mc)) {
|
|
pr_err("failed to allocate memory for mem_chunk\n");
|
|
ret = ERR_PTR(-ENOMEM);
|
|
goto free_free_list;
|
|
}
|
|
|
|
mc->address = mm_info->start_address;
|
|
mc->size = mm_info->size;
|
|
strlcpy(mc->name, "FREE", NAME_SIZE);
|
|
list_add(&mc->node, mm_info->free_list);
|
|
spin_lock_init(&mm_info->lock);
|
|
|
|
return (void *)mm_info;
|
|
|
|
free_free_list:
|
|
kfree(mm_info->free_list);
|
|
free_alloc_list:
|
|
kfree(mm_info->alloc_list);
|
|
free_mm_info:
|
|
kfree(mm_info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void destroy_mem_manager(void *mem_handle)
|
|
{
|
|
struct mem_manager_info *mm_info =
|
|
(struct mem_manager_info *)mem_handle;
|
|
struct mem_chunk *mc_last = NULL;
|
|
|
|
/* Clear all allocated memory */
|
|
clear_alloc_list(mm_info);
|
|
|
|
mc_last = list_entry((mm_info->free_list)->next,
|
|
struct mem_chunk, node);
|
|
list_del(&mc_last->node);
|
|
|
|
kfree(mc_last);
|
|
kfree(mm_info->alloc_list);
|
|
kfree(mm_info->free_list);
|
|
kfree(mm_info);
|
|
}
|