blob: 6f2d1dac18a7ba60f3443c28addc097687ade17f [file] [log] [blame]
/********************************************************************************
* Marvell GPL License Option
*
* If you received this File from Marvell, you may opt to use, redistribute and/or
* modify this File in accordance with the terms and conditions of the General
* Public License Version 2, June 1991 (the "GPL License"), a copy of which is
* available along with the File in the license.txt file or by writing to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or
* on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
* WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
* DISCLAIMED. The GPL License provides additional details about this warranty
* disclaimer.
********************************************************************************/
/*******************************************************************************
System head files
*/
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
/*******************************************************************************
Local head files
*/
#include "shm_type.h"
#include "memory_engine.h"
/*******************************************************************************
Device API
*/
int shm_device_create(shm_device_t ** device,
uint base, uint size, uint threshold)
{
int res;
shm_device_t *new_device;
shm_debug("shm_device_create start.\n");
if (device == NULL)
return -EINVAL;
new_device = kmalloc(sizeof(shm_device_t), GFP_KERNEL);
if (new_device == NULL)
return -ENOMEM;
memset(new_device, 0, sizeof(shm_device_t));
new_device->m_base = base;
new_device->m_size = size;
new_device->m_threshold = threshold;
res = memory_engine_create(&new_device->m_engine,
new_device->m_base,
new_device->m_size, new_device->m_threshold);
if (res != 0) {
shm_error("memory_engine_create failed. (%d)\n", res);
return res;
}
shm_trace("memory size (bytes) = 0x%08X\n",
new_device->m_size);
shm_trace("memory threshold (bytes) = 0x%08X\n",
new_device->m_threshold);
shm_trace("memory base phys addr = 0x%08X\n",
new_device->m_base);
*device = new_device;
return 0;
}
int shm_device_destroy(shm_device_t ** device)
{
int res;
shm_debug("shm_device_destroy start.\n");
if (device == NULL)
return -EINVAL;
res = memory_engine_destroy(&((*device)->m_engine));
if (res != 0) {
shm_error("memory_engine_destroy failed. (%d)\n", res);
return res;
}
kfree(*device);
*device = NULL;
shm_trace("shm_device_destroy OK.\n");
return 0;
}
int shm_device_get_meminfo(shm_device_t * device, pMV_SHM_MemInfo_t pInfo)
{
shm_debug("shm_device_get_meminfo start.\n");
if ((device == NULL) || (pInfo == NULL))
return -EINVAL;
pInfo->m_totalmem = device->m_engine->m_size;
pInfo->m_usedmem = device->m_engine->m_size_used;
pInfo->m_freemem = device->m_engine->m_size_free;
pInfo->m_peak_usedmem = device->m_engine->m_peak_usedmem;
pInfo->m_max_freeblock = device->m_engine->m_max_freeblock;
pInfo->m_min_freeblock = device->m_engine->m_min_freeblock;
pInfo->m_max_usedblock = device->m_engine->m_max_usedblock;
pInfo->m_min_usedblock = device->m_engine->m_min_usedblock;
pInfo->m_num_freeblock = device->m_engine->m_num_freeblock;
pInfo->m_num_usedblock = device->m_engine->m_num_usedblock;
return 0;
}
int shm_device_get_baseinfo(shm_device_t * device, pMV_SHM_BaseInfo_t pInfo)
{
shm_debug("shm_device_get_baseinfo start.\n");
if ((device == NULL) || (pInfo == NULL))
return -EINVAL;
pInfo->m_size = device->m_size;
pInfo->m_threshold = device->m_threshold;
pInfo->m_base_physaddr = device->m_base;
return 0;
}
int shm_device_get_node_info(shm_device_t *device,
shm_driver_operation_t *op)
{
memory_node_t *tmp;
if (device == NULL)
return -1;
mutex_lock(&(device->m_engine->m_mutex));
tmp = memory_engine_lookup_shm_node_for_size(
&(device->m_engine->m_shm_root), op->m_param1);
op->m_param2 = 0;
if (tmp == NULL) {
printk("shm_device_get_node_info fail"
" physaddress[%08lx]\n",op->m_param1);
mutex_unlock(&(device->m_engine->m_mutex));
return -1;
}
op->m_param1 = tmp->m_phyaddress;
op->m_param2 = MEMNODE_ALIGN_SIZE(tmp);
mutex_unlock(&(device->m_engine->m_mutex));
return 0;
}
int shm_device_check_test(shm_device_t *device,
shm_check_test_t *op)
{
int i = 0;
int res = 0;
memory_node_t *node = NULL;
user_node_t *usertmp = NULL;
if (device == NULL)
return -1;
mutex_lock(&(device->m_engine->m_mutex));
node = memory_engine_lookup_shm_node_for_size(
&device->m_engine->m_shm_root,
op->offset + device->m_engine->m_base);
if (node == NULL) {
op->node_alive = 0;
} else {
op->node_alive = 1;
switch (op->cmd) {
case SHM_TEST_GET_INFO:
op->takeover_flag = node->b_takeover;
op->physical = node->m_phyaddress;
op->malloc_refcount = node->m_task_free_flag;
op->malloc_taskid = node->m_taskid;
list_for_each_entry(usertmp, &node->m_user_list,
m_list) {
op->user_taskid[i] = usertmp->m_taskid;
i++;
if (i == 5)
break;
}
res = 0;
break;
}
}
mutex_unlock(&(device->m_engine->m_mutex));
return res;
}
int shm_device_add_reference_count(shm_device_t *device,
size_t offset)
{
pid_t taskid;
size_t phyaddress;
int res = 0;
memory_node_t *node = NULL;
user_node_t *tmp = NULL;
struct task_struct *grptask = NULL;
if (device == NULL) {
shm_error("shm_device_free device is NULL\n");
return -EINVAL;
}
phyaddress = offset + device->m_engine->m_base;
mutex_lock(&device->m_engine->m_mutex);
node = memory_engine_lookup_shm_node_for_size(
&device->m_engine->m_shm_root,
phyaddress);
if (node == NULL) {
printk("shm_device_add_reference_count fail 1"
" physaddress[%08x]\n", phyaddress);
res = -1;
goto EXIT;
}
if (node->b_takeover == 1) {
res = 0;
goto EXIT;
}
taskid = task_tgid_vnr(current);
if (taskid == node->m_taskid) {
if (node->memory_type == SHM_SECURE_CACHE ||
node->memory_type == SHM_SECURE_NONCACHE) {
node->m_refcount++;
node->m_reference_count++;
}
res = 0;
goto EXIT;
}
list_for_each_entry(tmp, &node->m_user_list, m_list) {
if (tmp->m_taskid == taskid) {
if (node->memory_type == SHM_SECURE_CACHE ||
node->memory_type == SHM_SECURE_NONCACHE) {
tmp->m_refcount++;
node->m_reference_count++;
}
res = 0;
goto EXIT;
}
}
tmp = kmalloc(sizeof(user_node_t), GFP_KERNEL);
if (tmp == NULL) {
res = -ENOMEM;
goto EXIT;
}
tmp->m_refcount = 1;
tmp->m_threadid = task_pid_vnr(current);
strncpy(tmp->m_threadname, current->comm, 16);
tmp->m_taskid = task_tgid_vnr(current);
grptask = pid_task(task_tgid(current), PIDTYPE_PID);
if (NULL != grptask) {
strncpy(tmp->m_taskname, grptask->comm, 16);
} else {
memset(tmp->m_taskname, 0, 16);
}
list_add(&tmp->m_list, &node->m_user_list);
node->m_reference_count++;
res = 0;
EXIT:
mutex_unlock(&device->m_engine->m_mutex);
return res;
}
int shm_device_show_detail(shm_device_t * device, struct seq_file *file)
{
shm_debug("shm_device_show_detail start.\n");
if ((device == NULL) || (file == NULL))
return -EINVAL;
/* Walk all nodes and print node info to buffer */
return memory_engine_show(device->m_engine, file);
}
uint shm_device_allocate(shm_device_t * device, uint size, uint align, int mem_type)
{
int res = 0;
uint offset = 0;
memory_node_t *node = NULL;
memory_engine_t *engine = NULL;
pid_t taskid = task_tgid_vnr(current);
shm_debug("shm_device_allocate start. (%u, %u)\n", size, align);
if (device == NULL)
return ERROR_SHM_MALLOC_FAILED;
engine = device->m_engine;
if (NULL == engine)
return ERROR_SHM_MALLOC_FAILED;
res = memory_engine_allocate(device->m_engine, size, align, &node);
if (res != 0) {
memory_engine_dump_stat(device->m_engine);
/* invoke lowmem killer to release less important task */
if (NULL != device->m_shrinker)
device->m_shrinker((void *)device, size);
shm_error("memory_engine_allocate failed, size:%d pid:%d\n", size, taskid);
return ERROR_SHM_MALLOC_FAILED;
}
offset = MEMNODE_ALIGN_ADDR(node) - device->m_engine->m_base;
node->memory_type = mem_type;
shm_debug("shm_device_allocate get offset (%u) OK\n", offset);
return offset;
}
int shm_device_free(shm_device_t * device, uint offset)
{
int res = 0;
uint alignaddr = 0;
pid_t taskid = task_tgid_vnr(current);
shm_debug("shm_device_free start. (%u)\n", offset);
if (device == NULL) {
shm_error("shm_device_free device is NULL pid:%d\n", taskid);
return -EINVAL;
}
alignaddr = device->m_engine->m_base + offset;
res = memory_engine_free(device->m_engine, alignaddr);
if (res != 0) {
return res;
}
shm_debug("shm_device_free offset (%u) OK\n", offset);
return 0;
}
int shm_device_cache(shm_device_t *device, uint cmd,
shm_driver_operation_t op, int mem_type)
{
int res = 0;
char tag_clean[] = "clean";
char tag_invalidate[] = "invalidate";
char tag_cleanAndinvalidate[] = "clean and invalidate";
char *ptr_tag;
if (device == NULL) {
shm_error("shm_device_free device is NULL\n");
return -EINVAL;
}
if ((mem_type == SHM_CACHE) || (mem_type == SHM_SECURE_CACHE)) {
res = memory_engine_cache(device->m_engine, cmd, op);
} else { // non-cache
res = -1;
if (cmd == SHM_DEVICE_CMD_INVALIDATE) {
ptr_tag = tag_invalidate;
} else if (cmd == SHM_DEVICE_CMD_CLEAN) {
ptr_tag = tag_clean;
} else {
ptr_tag = tag_cleanAndinvalidate;
}
printk("SHM noncache not need %s cache\n",ptr_tag);
}
return res;
}
int shm_device_takeover(shm_device_t* device, uint offset)
{
int res = 0;
uint alignaddr = 0;
shm_debug("shm_device_takeover start (%u)\n", offset);
if (NULL == device)
return -EINVAL;
alignaddr = device->m_engine->m_base + offset;
res = memory_engine_takeover(device->m_engine, alignaddr);
if (0 != res) {
return res;
}
shm_debug("shm_device_takeover offset (%u) OK\n", offset);
return 0;
}
int shm_device_giveup(shm_device_t *device, uint offset)
{
int res = 0;
uint alignaddr = 0;
shm_debug("shm_device_giveup start (%u)\n", offset);
if (NULL == device)
return -EINVAL;
alignaddr = device->m_engine->m_base + offset;
res = memory_engine_giveup(device->m_engine,alignaddr);
if (0 != res) {
return res;
}
shm_debug("shm_device_giveup offset (%u) OK\n", offset);
return 0;
}
int shm_device_get_stat(shm_device_t *device, struct shm_stat_info *stat)
{
int res = 0;
shm_debug("shm_device_get_stat start.\n");
if (NULL == device || NULL == stat)
return -EINVAL;
res = memory_engine_get_stat(device->m_engine, stat);
if (0 != res) {
return res;
}
shm_debug("shm_device_get_stat OK.\n");
return 0;
}
int shm_device_release_by_taskid(shm_device_t *device, pid_t taskid)
{
int res;
shm_debug("shm_device_release_by_taskid(%d) enter\n",taskid);
if (device == NULL)
return -EINVAL;
res = memory_engine_release_by_taskid(device, taskid);
if (res != 0) {
shm_error("memory_engine_release_by_taskid(%d) failed\n", taskid);
return res;
}
shm_debug("shm_device_release_by_taskid(%d) OK\n",taskid);
return 0;
}
int shm_device_dump_stat(shm_device_t *device)
{
int res = 0;
shm_debug("shm_device_dump_stat start.\n");
if (NULL == device)
return -EINVAL;
res = memory_engine_dump_stat(device->m_engine);
shm_debug("shm_device_dump_stat OK. \n");
return res;
}
int shm_device_show_stat(shm_device_t *device, struct seq_file *file)
{
int res = 0;
shm_debug("shm_device_show_stat start.\n");
if ((NULL == device) || (NULL == file))
return -EINVAL;
/* Walk all nodes and print node info to buffer */
res = memory_engine_show_stat(device->m_engine, file);
shm_debug("shm_device_show_stat OK. \n");
return res;
}