/*
 * mem.c
 *
 * memory operation for gp sharedmem.
 *
 * Copyright (c) 2012-2022 Huawei Technologies Co., Ltd.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * 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.
 */
#include "mem.h"
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
#include <linux/module.h>
#include <linux/mempool.h>
#include <linux/vmalloc.h>
#include <linux/of_reserved_mem.h>
#include <securec.h>
#include "smc_smp.h"
#include "tc_ns_client.h"
#include "teek_ns_client.h"
#include "agent.h"
#include "tc_ns_log.h"
#include "mailbox_mempool.h"
#include "internal_functions.h"
#include "reserved_mempool.h"

void tc_mem_free(struct tc_ns_shared_mem *shared_mem)
{
	if (!shared_mem)
		return;
	if (shared_mem->mem_type == RESERVED_TYPE) {
		reserved_mem_free(shared_mem->kernel_addr);
		kfree(shared_mem);
		return;
	}

	if (shared_mem->kernel_addr) {
		vfree(shared_mem->kernel_addr);
		shared_mem->kernel_addr = NULL;
	}
	kfree(shared_mem);
}

static void init_shared_mem(struct tc_ns_shared_mem *sh, void *addr, size_t len)
{
	sh->kernel_addr = addr;
	sh->len = (uint32_t)len;
	sh->user_addr = INVALID_MAP_ADDR;
	sh->user_addr_ca = INVALID_MAP_ADDR;
	atomic_set(&sh->usage, 0);
}
struct tc_ns_shared_mem *tc_mem_allocate(size_t len)
{
	struct tc_ns_shared_mem *shared_mem = NULL;
	void *addr = NULL;

	shared_mem = kmalloc(sizeof(*shared_mem), GFP_KERNEL | __GFP_ZERO);
	if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)shared_mem)) {
		tloge("shared_mem kmalloc failed\n");
		return ERR_PTR(-ENOMEM);
	}
	shared_mem->mem_type = VMALLOC_TYPE;
	len = ALIGN(len, SZ_4K);
	if (exist_res_mem()) {
		if (len > get_res_mem_slice_size()) {
			tloge("allocate reserved mem size too large\n");
			kfree(shared_mem);
			return ERR_PTR(-EINVAL);
		}
		addr = reserved_mem_alloc(len);
		if (addr) {
			shared_mem->mem_type = RESERVED_TYPE;
			init_shared_mem(shared_mem, addr, len);
			return shared_mem;
		} else {
			tlogw("no more reserved memory to alloc so we use system vmalloc.\n");
		}
	}
	if (len > MAILBOX_POOL_SIZE) {
		tloge("alloc sharemem size %zu is too large\n", len);
		kfree(shared_mem);
		return ERR_PTR(-EINVAL);
	}
	addr = vmalloc_user(len);
	if (!addr) {
		tloge("alloc mailbox failed\n");
		kfree(shared_mem);
		return ERR_PTR(-ENOMEM);
	}

	init_shared_mem(shared_mem, addr, len);
	return shared_mem;
}