blob: 063ada0bde3314b2919801456b0f606610ad4dea [file] [log] [blame]
/*
* drivers/amlogic/media/common/codec_mm/codec_mm_scatter.c
*
* Copyright (C) 2016 Amlogic, Inc. All rights reserved.
*
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/dma-contiguous.h>
#include <linux/cma.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/libfdt_env.h>
#include <linux/of_reserved_mem.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/genalloc.h>
#include <linux/dma-mapping.h>
#include <linux/dma-contiguous.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/codec_mm_scatter.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#include <linux/completion.h>
#include "codec_mm_priv.h"
#include "codec_mm_scatter_priv.h"
#include <trace/events/meson_atrace.h>
#define SCATTER_MEM "SCATTER_MEM"
/*#define PHY_ADDR_NEED_64BITS*/
#if PAGE_SHIFT >= 12
#define SID_MASK 0xfff
#elif PAGE_SHIFT >= 8
#define SID_MASK 0xff
#else
#error "unsupport PAGE_SHIFT PAGE_SHIFT must >= 8" ## PAGE_SHIFT
#endif
#define MAX_ADDR_SHIFT PAGE_SHIFT
#define MAX_SID (SID_MASK - 1)
#define MAX_HASH_SID (MAX_SID - 1)
#define ADDR_SEED(paddr) (((paddr) >> (MAX_ADDR_SHIFT << 1)) +\
((paddr) >> MAX_ADDR_SHIFT) +\
(paddr))
/*
*hash ID: 0-MAX_HASH_SID
*ONE_PAGE_SID: MAX_SID
*/
#define HASH_PAGE_ADDR(paddr) (ADDR_SEED(paddr) % MAX_SID)
#define SLOT_TO_SID(slot) HASH_PAGE_ADDR(((slot->phy_addr)>>PAGE_SHIFT))
#define ONE_PAGE_SID (SID_MASK)
#define PAGE_SID(mm_page) (page_sid_type)((mm_page) & SID_MASK)
#define PAGE_ADDR(mm_page) (ulong)(((mm_page) >> MAX_ADDR_SHIFT)\
<< MAX_ADDR_SHIFT)
#define ADDR2PAGE(addr, sid) (((addr) & (~SID_MASK)) | (sid))
#define PAGE_SID_OF_MMS(mms, id) PAGE_SID((mms)->pages_list[id])
#define PAGE_ADDR_OF_MMS(mms, id) PAGE_ADDR((mms)->pages_list[id])
#define INVALID_ID(mms, id) (!(mms) ||\
!(mms)->pages_list ||\
(mms)->page_cnt <= (id) ||\
(id) < 0)
#define SID_OF_ONEPAGE(sid) ((sid) == ONE_PAGE_SID)
#define ADDR2BIT(base, addr) (((addr) - (base)) >> PAGE_SHIFT)
#define BIT2ADDR(base, bit) ((base) + (1<<PAGE_SHIFT) * (bit))
#define VALID_SID(sid) (((sid) < MAX_SID) || SID_OF_ONEPAGE(sid))
#define VALID_BIT(slot, bit) (bit >= 0 && ((slot->pagemap_size << 3) > bit))
#define CODEC_MM_S_ERR(x) ((-1000) - (x))
/*#define SCATTER_DEBUG*/
#define ERR_LOG(args...) pr_err(args)
#define WAR_LOG(args...) pr_warn(args)
#define INFO_LOG(args...) pr_info(args)
#ifdef SCATTER_DEBUG
#define DBG_LOG(args...) pr_info(args)
#else
#define DBG_LOG(args...)
#endif
#define MAX_SYS_BLOCK_PAGE 128
#define MIN_SYS_BLOCK_PAGE 8
/*#define USE_KMALLOC_FOR_SCATTER*/
#ifdef USE_KMALLOC_FOR_SCATTER
#define SC_ALLOC(s, f) kmalloc(s, f)
#define SC_FREE(p) kfree(p)
#else
#define SC_ALLOC(s, f) vmalloc(s)
#define SC_FREE(p) vfree(p)
#endif
#define MAX_SC_LIST 64
#define MK_TAG(a, b, c, d) (((a) << 24) | ((b) << 16) |\
((c) << 8) | d)
#define SMGT_IDENTIFY_TAG MK_TAG('Z', 'S', 'C', 'Z')
struct codec_mm_scatter_s {
u32 keep_size_PAGE;
u32 reserved_block_mm_M;
u32 try_alloc_in_cma_page_cnt;
u32 try_alloc_in_sys_page_cnt_max;
u32 try_alloc_in_sys_page_cnt_min;
u32 enable_slot_from_sys;
u32 no_cache_size_M;
u32 support_from_slot_sys;
u32 no_alloc_from_sys;
};
struct codec_mm_scatter_mgt {
unsigned int tag;/*=*/
struct codec_mm_slot *slot_list_map[MAX_SID];
int tvp_mode;
int codec_mm_num;
int total_page_num;
int alloced_page_num;
int max_alloced;
u32 try_alloc_in_cma_page_cnt;
u32 try_alloc_in_sys_page_cnt;
u32 try_alloc_in_sys_page_cnt_max;
u32 try_alloc_in_sys_page_cnt_min;
int alloc_from_cma_first;
u32 enable_slot_from_sys;
u32 no_cache_size_M;
u32 no_alloc_from_sys;
u32 support_from_slot_sys;
int one_page_cnt;
int scatters_cnt;
int slot_cnt;
u32 reserved_block_mm_M;
u32 keep_size_PAGE;
int mem_flags;
int alloc_from_sys_sc_cnt;
int alloc_from_sys_page_cnt;
int alloc_from_sys_max_page_cnt;
int delay_free_on;
int force_cache_on;
int force_cache_page_cnt;
u64 delay_free_timeout_jiffies64;
/*time states*/
int alloc_max_us;
u64 alloc_total_us;
int alloc_cnt;
int alloc_10us_less_cnt;
int alloc_10_50us_cnt;
int alloc_50_100us_cnt;
int alloc_100_1000us_cnt;
int alloc_1_10ms_cnt;
int alloc_10_100ms_cnt;
int alloc_100ms_up_cnt;
/*free time states*/
int free_max_us;
u64 free_total_us;
int free_cnt;
int free_10us_less_cnt;
int free_10_50us_cnt;
int free_50_100us_cnt;
int free_100_1000us_cnt;
int free_1_10ms_cnt;
int free_10_100ms_cnt;
int free_100ms_up_cnt;
struct delayed_work dealy_work;
int scatter_task_run_num;
struct codec_mm_scatter *cache_scs[2];
int cached_pages;
spinlock_t list_lock;
struct mutex monitor_lock;
struct completion complete;
struct list_head free_list; /*slot */
struct list_head scatter_list; /*scatter list */
struct codec_mm_scatter *scmap[MAX_SC_LIST];/*used for valid check. */
};
#define is_cache_sc(smgt, mms) ((smgt->cache_scs[0] == mms) ||\
(smgt->cache_scs[1] == mms))
static struct codec_mm_scatter_s g_scatter;
static struct codec_mm_scatter_mgt *scatter_mgt;
static struct codec_mm_scatter_mgt *scatter_tvp_mgt;
static struct codec_mm_scatter_mgt *codec_mm_get_scatter_mgt(
int is_tvp)
{
if (is_tvp)
return scatter_tvp_mgt;
return scatter_mgt;
}
static int codec_mm_scatter_valid_locked(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *mms);
/*#define MY_MUTEX_DEBUG*/
#ifdef MY_MUTEX_DEBUG
#define codec_mm_scatter_lock(s) \
codec_mm_scatter_lock_debug(s, __LINE__)
#define codec_mm_list_lock(s) \
codec_mm_list_lock_debug(s, __LINE__)
static inline int mutex_trylock_time(
struct mutex *lock, int wait)
{
unsigned long timeout = jiffies + wait;
int locked = mutex_trylock(lock);
while (!locked && time_before(jiffies, timeout)) {
msleep(20);
locked = mutex_trylock(lock);
}
return locked;
}
#define TRY_MLOCK_INFO(lock, line, time, info)\
static int last_lock_line;\
while (!mutex_trylock_time((lock), time)) {\
pr_err(info " mutex has lock on %d,new lock on %d\n",\
last_lock_line, line);\
} \
last_lock_line = line;\
static inline int spin_trylock_time(
spinlock_t *lock, int wait)
{
unsigned long timeout = jiffies + wait;
int locked = spin_trylock(lock);
while (!locked && time_before(jiffies, timeout)) {
msleep(20);
locked = spin_trylock(lock);
}
return locked;
}
#define TRY_SLOCK_INFO(lock, line, time, info)\
static int last_lock_line;\
while (!spin_trylock_time((lock), time)) {\
pr_err(info " spin has lock on %d,new lock on %d\n",\
last_lock_line, line);\
} \
last_lock_line = line;\
static inline void codec_mm_scatter_lock_debug(
struct codec_mm_scatter *mms,
int line)
{
TRY_MLOCK_INFO(&mms->mutex, line, 10 * HZ, "mms");
}
static inline void codec_mm_list_lock_debug(
struct codec_mm_scatter_mgt *smgt, int line)
{
TRY_SLOCK_INFO(&smgt->list_lock, line, 10 * HZ, "list");
}
#else
static inline void codec_mm_scatter_lock(
struct codec_mm_scatter *mms)
{
mutex_lock(&mms->mutex);
}
static inline int codec_mm_scatter_trylock(
struct codec_mm_scatter *mms)
{
return mutex_trylock(&mms->mutex);
}
static inline void codec_mm_list_lock(
struct codec_mm_scatter_mgt *smgt)
{
spin_lock(&smgt->list_lock);
}
#endif
static inline void codec_mm_scatter_unlock(
struct codec_mm_scatter *mms)
{
mutex_unlock(&mms->mutex);
}
static inline void codec_mm_list_unlock(
struct codec_mm_scatter_mgt *smgt)
{
spin_unlock(&smgt->list_lock);
}
static int codec_mm_scatter_alloc_want_pages_in(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *mms,
int want_pages);
static struct workqueue_struct *codec_mm_scatter_wq_get(void)
{
static struct workqueue_struct *codec_mm_scatter_wq;
if (!codec_mm_scatter_wq) {
codec_mm_scatter_wq =
create_singlethread_workqueue("codec_mm_sc");
}
return codec_mm_scatter_wq;
}
static int codec_mm_schedule_delay_work(struct codec_mm_scatter_mgt *smgt,
int delay_ms, int for_update)
{
bool ret;
if (!for_update && delayed_work_pending(&smgt->dealy_work))
return 0;
if (delayed_work_pending(&smgt->dealy_work))
cancel_delayed_work(&smgt->dealy_work);
if (codec_mm_scatter_wq_get()) {
ret = queue_delayed_work(codec_mm_scatter_wq_get(),
&smgt->dealy_work, delay_ms * HZ / 1000);
} else
ret = schedule_delayed_work(&smgt->dealy_work,
delay_ms * HZ / 1000);
return ret;
}
static inline u64 codec_mm_get_current_us(void)
{
struct timeval tv;
do_gettimeofday(&tv);
return div64_u64(timeval_to_ns(&tv), 1000);
}
static void codec_mm_update_alloc_time(
struct codec_mm_scatter_mgt *smgt, u64 startus)
{
int spend_time_us;
spend_time_us = (int)(codec_mm_get_current_us() - startus);
if (spend_time_us > 0 && spend_time_us < 100000000) {
/* >0 && less than 100s*/
/*else think time base changed.*/
smgt->alloc_cnt++;
if (spend_time_us < 10)
smgt->alloc_10us_less_cnt++;
else if (spend_time_us < 50)
smgt->alloc_10_50us_cnt++;
else if (spend_time_us < 100)
smgt->alloc_50_100us_cnt++;
else if (spend_time_us < 1000)
smgt->alloc_100_1000us_cnt++;
else if (spend_time_us < 10000)
smgt->alloc_1_10ms_cnt++;
else if (spend_time_us < 100000)
smgt->alloc_10_100ms_cnt++;
else
smgt->alloc_100ms_up_cnt++;
smgt->alloc_total_us += spend_time_us;
if (spend_time_us > smgt->alloc_max_us) {
/*..*/
smgt->alloc_max_us = spend_time_us;
}
}
}
static void codec_mm_update_free_time(
struct codec_mm_scatter_mgt *smgt, u64 startus)
{
int spend_time_us;
spend_time_us = (int)(codec_mm_get_current_us() - startus);
if (spend_time_us > 0 && spend_time_us < 100000000) {
/* >0 && less than 100s*/
/*else think time base changed.*/
smgt->free_cnt++;
if (spend_time_us < 10)
smgt->free_10us_less_cnt++;
else if (spend_time_us < 50)
smgt->free_10_50us_cnt++;
else if (spend_time_us < 100)
smgt->free_50_100us_cnt++;
else if (spend_time_us < 1000)
smgt->free_100_1000us_cnt++;
else if (spend_time_us < 10000)
smgt->free_1_10ms_cnt++;
else if (spend_time_us < 100000)
smgt->free_10_100ms_cnt++;
else
smgt->free_100ms_up_cnt++;
smgt->free_total_us += spend_time_us;
if (spend_time_us > smgt->free_max_us) {
/*..*/
smgt->free_max_us = spend_time_us;
}
}
}
static void codec_mm_clear_alloc_infos_in(
struct codec_mm_scatter_mgt *smgt)
{
smgt->alloc_cnt = 0;
smgt->alloc_10us_less_cnt = 0;
smgt->alloc_10_50us_cnt = 0;
smgt->alloc_50_100us_cnt = 0;
smgt->alloc_100_1000us_cnt = 0;
smgt->alloc_1_10ms_cnt = 0;
smgt->alloc_10_100ms_cnt = 0;
smgt->alloc_100ms_up_cnt = 0;
smgt->alloc_total_us = 0;
smgt->alloc_max_us = 0;
smgt->free_cnt = 0;
smgt->free_10us_less_cnt = 0;
smgt->free_10_50us_cnt = 0;
smgt->free_50_100us_cnt = 0;
smgt->free_100_1000us_cnt = 0;
smgt->free_1_10ms_cnt = 0;
smgt->free_10_100ms_cnt = 0;
smgt->free_100ms_up_cnt = 0;
smgt->free_total_us = 0;
smgt->free_max_us = 0;
}
void codec_mm_clear_alloc_infos(void)
{
codec_mm_clear_alloc_infos_in(codec_mm_get_scatter_mgt(0));
codec_mm_clear_alloc_infos_in(codec_mm_get_scatter_mgt(1));
}
#if 0
static int codec_mm_slot_get_info(struct codec_mm_scatter_mgt *smgt,
int *free_pages, int *slot_num, int *max_sg_pages)
{
struct codec_mm_slot *slot;
int total_pages = 0;
int alloced_pages = 0;
int slot_used_num = 0;
int max_free_pages_sg = 0;
codec_mm_list_lock(smgt);
if (!list_empty(&smgt->free_list)) {
struct list_head *header, *list;
header = &smgt->free_list;
list = header->prev;
while (list != header) {
slot = list_entry(list, struct codec_mm_slot,
free_list);
total_pages += slot->page_num;
alloced_pages += slot->alloced_page_num;
slot_used_num++;
if (slot->page_num - slot->alloced_page_num >
max_free_pages_sg)
max_free_pages_sg = slot->page_num -
slot->alloced_page_num;
list = list->prev;
};
}
codec_mm_list_unlock(smgt);
if (total_pages < alloced_pages)
return 0;
*free_pages = total_pages - alloced_pages;
*slot_num = slot_used_num;
*max_sg_pages = max_free_pages_sg;
return 0;
}
#endif
static int codec_mm_set_slot_in_hash(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_slot *slot)
{
page_sid_type sid = SLOT_TO_SID(slot);
if (sid > MAX_SID) {
ERR_LOG("ERROR sid %d", sid);
return -1;
}
slot->sid = sid;
INIT_LIST_HEAD(&slot->sid_list);
INIT_LIST_HEAD(&slot->free_list);
codec_mm_list_lock(smgt);
if (!smgt->slot_list_map[sid]) {
smgt->slot_list_map[sid] = slot;
slot->isroot = 1;
} else {
struct codec_mm_slot *f_slot = smgt->slot_list_map[sid];
list_add_tail(&slot->sid_list, &f_slot->sid_list);
slot->isroot = 0;
}
smgt->total_page_num += slot->page_num;
smgt->slot_cnt++;
if (slot->from_type == SLOT_FROM_GET_FREE_PAGES) {
smgt->alloc_from_sys_sc_cnt++;
smgt->alloc_from_sys_page_cnt += slot->page_num;
if (smgt->alloc_from_sys_page_cnt >
smgt->alloc_from_sys_max_page_cnt)
smgt->alloc_from_sys_max_page_cnt =
smgt->alloc_from_sys_page_cnt;
}
list_add_tail(&slot->free_list, &smgt->free_list);
codec_mm_list_unlock(smgt);
return 0;
}
static struct codec_mm_slot *codec_mm_find_slot_in_hash(
struct codec_mm_scatter_mgt *smgt,
page_sid_type sid, ulong addr)
{
struct codec_mm_slot *fslot, *slot;
if (!VALID_SID(sid) || SID_OF_ONEPAGE(sid))
return NULL;
codec_mm_list_lock(smgt);
fslot = smgt->slot_list_map[sid];
if (!fslot) {
ERR_LOG("not valid sid %d\n",
(int)sid);
goto err;
}
slot = fslot;
while (!(addr >= slot->phy_addr && /*optimization with hash? */
addr <
(slot->phy_addr +
(slot->page_num << PAGE_SHIFT)))) {
/*not in range. */
slot = list_entry(slot->sid_list.prev,
struct codec_mm_slot, sid_list);
/*
*pr_err("Slot range from =%p->%p\n",
*(void *)slot->phy_addr,
*(void *)slot->phy_addr +
*(slot->page_num << PAGE_SHIFT));
*/
if (slot == fslot) {
ERR_LOG("can't find valid slot, for addr =%p\n",
(void *)addr);
goto err;
}
}
codec_mm_list_unlock(smgt);
return slot;
err:
codec_mm_list_unlock(smgt);
return NULL;
}
static int codec_mm_slot_free(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_slot *slot)
{
int ret = 0;
codec_mm_list_lock(smgt);
if (slot->alloced_page_num > 0 || slot->on_alloc_free) {
codec_mm_list_unlock(smgt);
return -1;
}
if (!list_empty(&slot->free_list))
list_del(&slot->free_list);
if (!list_empty(&slot->sid_list)) {
if (slot->isroot) {
struct codec_mm_slot *next_slot;
next_slot = list_entry(slot->sid_list.next,
struct codec_mm_slot, sid_list);
next_slot->isroot = 1;
smgt->slot_list_map[slot->sid] = next_slot;
}
list_del(&slot->sid_list);
} else { /*no sid list,clear map */
smgt->slot_list_map[slot->sid] = NULL;
}
smgt->slot_cnt--;
smgt->total_page_num -= slot->page_num;
if (slot->from_type == SLOT_FROM_GET_FREE_PAGES) {
smgt->alloc_from_sys_sc_cnt--;
smgt->alloc_from_sys_page_cnt -= slot->page_num;
}
codec_mm_list_unlock(smgt);
switch (slot->from_type) {
case SLOT_FROM_CODEC_MM:
if (slot->mm)
codec_mm_release(slot->mm, SCATTER_MEM);
else
ERR_LOG("ERR:slot->mm is ERROR:%p\n", slot->mm);
break;
case SLOT_FROM_GET_FREE_PAGES:
if (slot->page_header != 0)
free_pages(slot->page_header,
get_order(PAGE_SIZE * slot->page_num));
else
ERR_LOG("ERR:slot->page_header is ERROR:%p\n",
(void *)slot->page_header);
break;
/*other */
default:
ERR_LOG("unknown from type:%d\n", slot->from_type);
ret = -1;
}
kfree(slot->pagemap);
kfree(slot);
return 0;
}
static int codec_mm_slot_try_free(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_slot *slot)
{
if (smgt->keep_size_PAGE > 0) {
/*delay free, when size < delay_free_M MB */
int free_pages = smgt->total_page_num - smgt->alloced_page_num;
if (free_pages < smgt->keep_size_PAGE)
return -1;
}
return codec_mm_slot_free(smgt, slot);
}
static inline int codec_mm_slot_init_bitmap(struct codec_mm_slot *slot)
{
slot->alloced_page_num = 0;
/*bytes = 8bits for 8 pages.
*1 more for less than 8 page.
*another for reserved.
*/
slot->pagemap_size = (slot->page_num >> 3) + 2;
slot->pagemap = kmalloc(slot->pagemap_size, GFP_KERNEL);
if (!slot->pagemap) {
ERR_LOG("ERROR.init pagemap failed\n");
return -1;
}
memset(slot->pagemap, 0, slot->pagemap_size);
slot->next_bit = 0;
return 0;
}
/*
*flags : 1. don't used codecmm.
*/
static struct codec_mm_slot *codec_mm_slot_alloc(
struct codec_mm_scatter_mgt *smgt,
int size, int flags)
{
struct codec_mm_slot *slot;
struct codec_mm_s *mm;
int try_alloc_size = size;
int have_alloced = 0;
if (try_alloc_size > 0 && try_alloc_size <= PAGE_SIZE)
return NULL; /*don't alloc less than one PAGE. */
slot = kmalloc(sizeof(struct codec_mm_slot), GFP_KERNEL);
if (!slot)
return NULL;
memset(slot, 0, sizeof(struct codec_mm_slot));
do {
if (flags & 1)
break; /*ignore codec_mm */
if ((try_alloc_size <= 0 ||
try_alloc_size > 64 * 1024) && /*must > 512K. */
(smgt->tvp_mode ||
(codec_mm_get_free_size() >
smgt->reserved_block_mm_M * SZ_1M))) {
/*try from codec_mm */
if (try_alloc_size <= 0) {
try_alloc_size =
smgt->try_alloc_in_cma_page_cnt *
PAGE_SIZE;
}
if (codec_mm_get_free_size() < try_alloc_size)
try_alloc_size = codec_mm_get_free_size();
mm = codec_mm_alloc(SCATTER_MEM, try_alloc_size, 0,
CODEC_MM_FLAGS_FOR_VDECODER |
CODEC_MM_FLAGS_FOR_SCATTER |
(smgt->tvp_mode ?
CODEC_MM_FLAGS_TVP : 0)
);
if (mm != NULL) {
slot->from_type = SLOT_FROM_CODEC_MM;
slot->mm = mm;
slot->page_num = mm->page_count;
slot->phy_addr = mm->phy_addr;
codec_mm_slot_init_bitmap(slot);
if (slot->pagemap == NULL) {
codec_mm_release(mm, SCATTER_MEM);
break; /*try next. */
}
have_alloced = 1;
DBG_LOG("alloced from codec mm %d!!!\n",
slot->page_num);
}
}
} while (0);
if (!have_alloced && !smgt->support_from_slot_sys) {
/*not enabled from sys */
goto error;
}
if (!have_alloced) { /*init for sys alloc */
if (size <= 0)
try_alloc_size =
smgt->try_alloc_in_sys_page_cnt << PAGE_SHIFT;
else
try_alloc_size = PAGE_ALIGN(size);
if (try_alloc_size <= PAGE_SIZE << 1) {
DBG_LOG("try too small %d, try one page now,\n",
try_alloc_size);
goto error; /*don't alloc 1 page with slot. */
}
}
if (!have_alloced) {
/*tvp not support alloc from sys.*/
if (smgt->tvp_mode || smgt->no_alloc_from_sys)
goto error;
}
while (!have_alloced) {
/*don't alloc 1 page with slot. */
/*try alloc from sys. */
int page_order = get_order(try_alloc_size);
slot->page_header = __get_free_pages(
__GFP_IO | __GFP_NOWARN | __GFP_NORETRY,
page_order);
if (!slot->page_header) {
if ((try_alloc_size >> (PAGE_SHIFT + 1)) >=
smgt->try_alloc_in_sys_page_cnt_min) {
try_alloc_size = try_alloc_size >> 1;
smgt->try_alloc_in_sys_page_cnt =
try_alloc_size >> PAGE_SHIFT;
continue; /*try less block memory. */
} else {
/*disabled from sys
*may auto enabled when free more.
*/
smgt->support_from_slot_sys = 0;
/*
* ERR_LOG("alloc sys failed size =%d!!!\n",
* try_alloc_size);
*/
goto error;
}
}
slot->from_type = SLOT_FROM_GET_FREE_PAGES;
slot->mm = NULL;
slot->page_num = 1 << page_order;
slot->phy_addr =
virt_to_phys((unsigned long *)slot->page_header);
codec_mm_slot_init_bitmap(slot);
if (slot->pagemap == NULL) {
free_pages(slot->page_header, page_order);
goto error;
}
have_alloced = 1;
break;
}
codec_mm_set_slot_in_hash(smgt, slot);
return slot;
error:
kfree(slot);
return NULL;
}
static int codec_mm_slot_free_page(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_slot *slot, ulong phy_addr)
{
int bit;
if (!slot || !slot->pagemap || slot->page_num <= 0)
return CODEC_MM_S_ERR(4);
bit = ADDR2BIT(slot->phy_addr, phy_addr);
if (!VALID_BIT(slot, bit))
return CODEC_MM_S_ERR(5); /*!!!out of page map.!! */
codec_mm_list_lock(smgt);
slot->on_alloc_free++;
if (!test_and_clear_bit(bit, slot->pagemap)) {
ERR_LOG("ERROR,page is ready free before!!! page=%p\n",
(void *)phy_addr);
slot->on_alloc_free--;
codec_mm_list_unlock(smgt);
return CODEC_MM_S_ERR(6);
}
slot->alloced_page_num--;
slot->on_alloc_free--;
codec_mm_list_unlock(smgt);
return 0;
}
static int codec_mm_slot_alloc_pages(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_slot *slot,
phy_addr_type *pages, int num)
{
int alloced = 0;
int need = num;
int can_alloced;
phy_addr_type page;
int tryn;
int i;
if (!slot || !slot->pagemap)
return -1;
if (slot->alloced_page_num >= slot->page_num)
return -2;
tryn = slot->page_num;
can_alloced = slot->page_num - slot->alloced_page_num;
need = need > can_alloced ? can_alloced : need;
i = slot->next_bit;
/*if not one alloc free. quit this one */
while (need > 0 && (slot->on_alloc_free == 1)) {
if (!VALID_BIT(slot, i)) {
ERR_LOG("ERROR alloc in slot %p\n",
slot);
ERR_LOG("\ti=%d,slot->pagemap=%p\n",
i,
slot->pagemap);
break;
}
codec_mm_list_lock(smgt);
if (!test_and_set_bit(i, slot->pagemap)) {
page = ADDR2PAGE(BIT2ADDR(slot->phy_addr, i),
slot->sid);
slot->alloced_page_num++;
pages[alloced] = page;
alloced++;
need--;
}
codec_mm_list_unlock(smgt);
i++;
if (i >= slot->page_num)
i = 0;
if (--tryn <= 0)
break;
}
slot->next_bit = i;
if (i >= slot->page_num)
slot->next_bit = 0;
DBG_LOG("alloced from %p, %d,%d,%d\n",
slot, slot->page_num,
slot->alloced_page_num, alloced);
return alloced;
}
static inline phy_addr_type codec_mm_get_page_addr(
struct codec_mm_scatter *mms,
int id)
{
phy_addr_type page;
if (INVALID_ID(mms, id))
return 0;
page = mms->pages_list[id];
return PAGE_ADDR(page);
}
static inline page_sid_type codec_mm_get_page_sid(
struct codec_mm_scatter *mms,
int id)
{
phy_addr_type page;
if (INVALID_ID(mms, id))
return 0;
page = mms->pages_list[id];
return PAGE_SID(page);
}
static int codec_mm_page_free_to_slot(
struct codec_mm_scatter_mgt *smgt,
page_sid_type sid, ulong addr)
{
struct codec_mm_slot *slot;
int ret;
int slot_free = 0;
slot = codec_mm_find_slot_in_hash(smgt, sid, addr);
if (!slot)
return -1;
ret = codec_mm_slot_free_page(smgt, slot, addr);
if (ret != 0)
ERR_LOG("free slot addr error =%p ret=%d\n",
(void *)addr, ret);
if (slot->alloced_page_num == 0)
slot_free = (codec_mm_slot_try_free(smgt, slot) == 0);
if (!slot_free) { /*move to have free list. */
codec_mm_list_lock(smgt);
if (list_empty(&slot->free_list) &&
(slot->alloced_page_num < slot->page_num)) {
DBG_LOG("add to free %p, %d,%d,%d\n",
slot, slot->page_num,
slot->alloced_page_num, ret);
list_add_tail(&slot->free_list, &smgt->free_list);
}
codec_mm_list_unlock(smgt);
}
return 0;
}
static int codec_mm_page_alloc_from_one_pages(
struct codec_mm_scatter_mgt *smgt,
phy_addr_type *pages, int num)
{
int neednum = num;
int alloced = 0;
while (neednum > 0) { /*one page alloc */
void *vpage = (void *)__get_free_page(GFP_KERNEL);
ulong page;
page_sid_type sid;
if (vpage != NULL) {
page = virt_to_phys(vpage);
sid = ONE_PAGE_SID;
page |= sid;
pages[alloced++] = page;
neednum--;
} else {
/*can't alloced memofy from ONEPAGE alloc */
WAR_LOG("Out of memory OnePage alloc =%d,%d\n",
alloced, num);
break;
}
}
codec_mm_list_lock(smgt);
smgt->total_page_num += alloced;
smgt->one_page_cnt += alloced;
smgt->alloced_page_num += alloced;
codec_mm_list_unlock(smgt);
return alloced;
}
static int codec_mm_page_alloc_from_slot(
struct codec_mm_scatter_mgt *smgt,
phy_addr_type *pages, int num)
{
struct codec_mm_slot *slot = NULL;
int alloced = 0;
int neednum = num;
int n;
codec_mm_list_lock(smgt);
if (!smgt->tvp_mode &&
list_empty(&smgt->free_list) &&
(codec_mm_get_free_size() </*no codec mm*/
smgt->reserved_block_mm_M * SZ_1M) &&
!smgt->support_from_slot_sys) {/*no sys*/
codec_mm_list_unlock(smgt);
return 0;
}
codec_mm_list_unlock(smgt);
do {
slot = NULL;
if (smgt->total_page_num <= 0 || /*no codec mm. */
smgt->alloced_page_num == smgt->total_page_num ||
list_empty(&smgt->free_list)) {
/*codec_mm_scatter_info_dump(NULL, 0);*/
slot = codec_mm_slot_alloc(smgt, 0, 0);
if (!slot) {
u32 alloc_pages =
smgt->try_alloc_in_cma_page_cnt/4;
/*
*ERR_LOG("can't alloc slot from system\n");
*/
if (codec_mm_get_sc_debug_mode() &
0x01) {
pr_info("alloc default cma size fail, try %d pages\n",
alloc_pages);
}
slot = codec_mm_slot_alloc(smgt,
alloc_pages * PAGE_SIZE, 0);
if (!slot) {
if (codec_mm_get_sc_debug_mode() &
0x01) {
pr_info("slot alloc 4M fail!\n");
}
break;
}
}
}
codec_mm_list_lock(smgt);
if (slot && slot->on_alloc_free != 0) {
ERR_LOG("the slot on alloc/free1: %d\n",
slot->on_alloc_free);
slot = NULL; /*slot used on another alloc. */
}
if (!slot && !list_empty(&smgt->free_list)) {
slot = list_entry(smgt->free_list.next,
struct codec_mm_slot, free_list);
if (!slot)
ERR_LOG("ERROR!!!!.slot is NULL!!!!\n");
else if (slot->on_alloc_free != 0) {
ERR_LOG("the slot on alloc/free: %d\n",
slot->on_alloc_free);
slot = NULL;
}
}
if (slot && slot->on_alloc_free == 0) { /*del from free list. */
slot->on_alloc_free++;
list_del_init(&slot->free_list);
} else {
slot = NULL;
}
codec_mm_list_unlock(smgt);
if (slot) {
n = codec_mm_slot_alloc_pages(smgt,
slot,
&pages[alloced], neednum);
codec_mm_list_lock(smgt);
slot->on_alloc_free--; /*alloc use end */
if (slot->alloced_page_num < slot->page_num &&
list_empty(&slot->free_list)) {
DBG_LOG("slot have free: %p, t:%d,a:%d,%d\n",
slot, slot->page_num,
slot->alloced_page_num, alloced);
list_add_tail(&slot->free_list,
&smgt->free_list);
/*no free now, del from free list.,*/
/*and init to empty */
}
codec_mm_list_unlock(smgt);
if (n > 0) {
alloced += n;
neednum -= n;
} else {
DBG_LOG("alloced fail neednum:%d n=%d\n",
neednum, n);
DBG_LOG("smgt->free_list.next:%p\n",
smgt->free_list.next);
DBG_LOG("smgt->free_list.prev:%p\n",
smgt->free_list.prev);
DBG_LOG("slot->free_list:%p\n",
&slot->free_list);
DBG_LOG("slot->free_list.next:%p\n",
slot->free_list.next);
/*codec_mm_dump_slot(slot, NULL, 0); */
continue; /*try next. */
}
} else {
/*not alloced enough in block mode, try one page next */
/*ERR_LOG("get free slot failed neednum: %d\n",
*neednum);
*/
break;
}
} while (neednum > 0);
if (neednum > 0 && 0) {
WAR_LOG("1can't alloc enough pages!!alloced=%d,need=%d\n",
alloced, num);
WAR_LOG("2can't alloc enough pages!!alloced=%d,need=%d\n",
alloced, num);
}
codec_mm_list_lock(smgt);
smgt->alloced_page_num += alloced;
if (smgt->max_alloced < smgt->alloced_page_num)
smgt->max_alloced = smgt->alloced_page_num;
codec_mm_list_unlock(smgt);
return alloced;
}
/*flags & 1; alloc.*/
static struct codec_mm_scatter *codec_mm_get_next_cache_scatter(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *cur_mms,
int flags)
{
struct codec_mm_scatter *mms = NULL;
codec_mm_list_lock(smgt);
if (cur_mms) {
if (smgt->cache_scs[0] == cur_mms)
mms = smgt->cache_scs[1];
else
mms = smgt->cache_scs[0];
} else if (smgt->cache_scs[0] && smgt->cache_scs[1]) {
int more_pages = flags & 1;
int id = -1;
if (smgt->cache_scs[0]->page_cnt >=
smgt->cache_scs[1]->page_cnt)
id = more_pages ? 0 : 1;
else
id = more_pages ? 1 : 0;
mms = smgt->cache_scs[id];
} else {
if (smgt->cache_scs[0])
mms = smgt->cache_scs[0];
else
mms = smgt->cache_scs[1];
}
codec_mm_list_unlock(smgt);
return mms;
}
static int codec_mm_page_alloc_from_free_scatter(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *src_mms,
phy_addr_type *pages, int num, int wait)
{
struct codec_mm_scatter *mms;
int need = num;
int alloced = 0;
mms = src_mms;
if (!mms)
return 0;
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_cache_scatter_1);
if (!wait) {
if (!codec_mm_scatter_trylock(mms))
return 0;/*mms is locked try another.*/
} else
codec_mm_scatter_lock(mms);
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_cache_scatter_2);
alloced = min(mms->page_cnt, need);
if (alloced > 0) {
mms->page_cnt -= alloced;
mms->page_tail -= alloced;
#if 1
memcpy(pages, &mms->pages_list[mms->page_tail + 1],
alloced * sizeof(phy_addr_type));
#else
/*alloc from first*/
memcpy(pages, &mms->pages_list[0],
alloced * sizeof(phy_addr_type));
memmove(&mms->pages_list[0],
&mms->pages_list[alloced],
mms->page_cnt * sizeof(phy_addr_type));
#endif
memset(&mms->pages_list[mms->page_tail + 1], 0,
alloced * sizeof(phy_addr_type));
}
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_cache_scatter_3);
codec_mm_list_lock(smgt);
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_cache_scatter_4);
smgt->cached_pages -= alloced;
codec_mm_list_unlock(smgt);
codec_mm_scatter_unlock(mms);
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_cache_scatter_5);
return alloced;
}
static int codec_mm_page_alloc_from_cache_scatter(
struct codec_mm_scatter_mgt *smgt,
phy_addr_type *pages, int num)
{
struct codec_mm_scatter *src_mms;
int alloced;
src_mms = codec_mm_get_next_cache_scatter(smgt, NULL, 1);
alloced = codec_mm_page_alloc_from_free_scatter(smgt,
src_mms, pages, num, 0);
if (alloced < num) {
src_mms = codec_mm_get_next_cache_scatter(smgt, src_mms, 1);
alloced += codec_mm_page_alloc_from_free_scatter(smgt,
src_mms, &pages[alloced], num - alloced, 1);
}
return alloced;
}
static int codec_mm_page_alloc_all_locked(
struct codec_mm_scatter_mgt *smgt,
phy_addr_type *pages, int num, int iscache)
{
int alloced = 0;
int can_from_scatter = iscache ? 0 : 1;
int can_from_slot = 1;
int new_alloc;
while (alloced < num) {
new_alloc = 0;
if (can_from_scatter) {
ATRACE_COUNTER("mmu alloc",
MMU_ALLOC_from_free_scatter);
new_alloc = codec_mm_page_alloc_from_cache_scatter(
smgt,
pages + alloced,
num - alloced);
ATRACE_COUNTER("mmu alloc",
MMU_ALLOC_from_free_scatter_end);
if (new_alloc <= 0)
can_from_scatter = 0;
} else if (can_from_slot) {
ATRACE_COUNTER("mmu alloc",
MMU_ALLOC_from_slot);
new_alloc = codec_mm_page_alloc_from_slot(
smgt,
pages + alloced,
num - alloced);
ATRACE_COUNTER("mmu alloc",
MMU_ALLOC_from_slot_end);
if (new_alloc <= 0)
can_from_slot = 0;
} else if (!smgt->no_alloc_from_sys && !smgt->tvp_mode) {
new_alloc = codec_mm_page_alloc_from_one_pages(
smgt,
pages + alloced,
num - alloced);
if (new_alloc <= 0)
break;
} else {
break;
}
alloced += new_alloc;
}
return alloced;
}
static int codec_mm_pages_free_to_scatter(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *src_mms,
struct codec_mm_scatter *dst_mms,
int wait)
{
int moved = 0;
int left;
if (src_mms->page_used >= src_mms->page_cnt)
return -1; /*no need free. */
if (!dst_mms || src_mms == dst_mms)
return 0;
ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_DTS);
if (!wait) {
if (!codec_mm_scatter_trylock(dst_mms))
return -2;/*mms is locked try another.*/
} else
codec_mm_scatter_lock(dst_mms);
ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_DTS_LOCK);
moved = min(src_mms->page_cnt - src_mms->page_used,
dst_mms->page_max_cnt - dst_mms->page_cnt);
left = src_mms->page_cnt - moved;
if (moved > 0) {
memcpy(&dst_mms->pages_list[dst_mms->page_tail + 1],
&src_mms->pages_list[left],
moved * sizeof(phy_addr_type));
memset(&src_mms->pages_list[left], 0,
sizeof(phy_addr_type) * moved);
}
dst_mms->page_cnt += moved;
dst_mms->page_tail += moved;
src_mms->page_cnt -= moved;
src_mms->page_tail -= moved;
ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_SMGT);
codec_mm_list_lock(smgt);
ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_SMGT_LOCK);
smgt->cached_pages += moved;
codec_mm_list_unlock(smgt);
ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_SMGT_LOCK_DONE);
codec_mm_scatter_unlock(dst_mms);
ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_DTS_LOCK_DONE);
return moved;
}
static int codec_mm_page_free_to_cache_scatter(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *src_mms)
{
struct codec_mm_scatter *dst_mms;
int alloced;
dst_mms = codec_mm_get_next_cache_scatter(smgt, NULL, 0);
alloced = codec_mm_pages_free_to_scatter(smgt, src_mms,
dst_mms, 0);
if (alloced == -2) {
dst_mms = codec_mm_get_next_cache_scatter(smgt, dst_mms, 0);
alloced += codec_mm_pages_free_to_scatter(smgt, src_mms,
dst_mms, 1);
}
return alloced;
}
/*
*free one page in mms;
*/
static int codec_mm_scatter_free_page_id_locked(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *mms, int id)
{
page_sid_type sid;
int ret;
if (INVALID_ID(mms, id))
return CODEC_MM_S_ERR(1);
sid = PAGE_SID_OF_MMS(mms, id);
if (!VALID_SID(sid))
return CODEC_MM_S_ERR(2);
if (SID_OF_ONEPAGE(sid)) {
ulong phy_addr = PAGE_ADDR_OF_MMS(mms, id);
free_page((unsigned long)phys_to_virt(phy_addr));
codec_mm_list_lock(smgt);
smgt->one_page_cnt--;
smgt->total_page_num--;
codec_mm_list_unlock(smgt);
if (id == mms->page_tail)
mms->page_tail--;
mms->page_cnt--;
return 0;
}
ret = codec_mm_page_free_to_slot(smgt, sid, PAGE_ADDR_OF_MMS(mms, id));
if (!ret) {
mms->page_cnt--;
mms->page_tail--;
}
return ret;
}
/*
*free one page in mms;
*/
static int codec_mm_scatter_free_pages_in_locked(
struct codec_mm_scatter *mms,
int start_id)
{
struct codec_mm_scatter_mgt *smgt =
(struct codec_mm_scatter_mgt *)mms->manager;
int i;
int ret;
int id = start_id;
int freeNum = 0;
int not_continue_print = 1;
for (i = mms->page_tail; i >= id; i--) {
ret = codec_mm_scatter_free_page_id_locked(smgt, mms, i);
if (ret < 0) {
if (not_continue_print) {
ERR_LOG("page free error.%d,id=%d, addr:%d\n",
ret, i,
(int)mms->pages_list[i]);
codec_mm_dump_scatter(mms, NULL, 0);
}
not_continue_print = 0;
} else {
not_continue_print = 1;
}
freeNum++;
mms->pages_list[i] = (phy_addr_type) 0;
}
codec_mm_list_lock(smgt);
smgt->alloced_page_num -= freeNum;
if (is_cache_sc(smgt, mms))
smgt->cached_pages -= freeNum;
codec_mm_list_unlock(smgt);
return 0;
}
/*
*fast_mode:
*1: set page_used: call by owner
*2: free to scatter: call by owner
*3: free to slot or others: call by owner
*
*free_mode:
*0: free from id pages.
*1: free not used pages;call by moniter.
*2: less pages.: call by moniter for cache
* id is negative.
*/
static int codec_mm_scatter_free_tail_pages_in(
struct codec_mm_scatter *mms,
int id,
int fast_mode,
int free_mode)
{
struct codec_mm_scatter_mgt *smgt;
int start_free_id = id;
if (!mms)
return -1;
ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START);
codec_mm_scatter_lock(mms);
ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_LOCK);
if (free_mode == 1)
start_free_id = mms->page_used;
if (free_mode == 2) {
if (id > 0) {
if (id >= mms->page_cnt)
start_free_id = 0;
else
start_free_id = mms->page_cnt - id;
} else
start_free_id = -1;
}
if ((start_free_id < 0) ||
(start_free_id >= mms->page_cnt) ||
(mms->page_tail < 0)) {
if (mms &&
start_free_id != mms->page_cnt) {
ERR_LOG(
"mms[%p],free error id %d(%d),cnt=%d ,=m:%d,%d\n",
mms,
id,
start_free_id,
mms->page_cnt,
fast_mode,
free_mode);
}
codec_mm_scatter_unlock(mms);
ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_LOCK_DONE);
return -1;
}
smgt = (struct codec_mm_scatter_mgt *)mms->manager;
codec_mm_list_lock(smgt);
mms->page_used = start_free_id;
codec_mm_list_unlock(smgt);
if (fast_mode == 1) {
codec_mm_scatter_unlock(mms);
ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_LOCK_DONE);
return 0;
}
if (fast_mode == 2 || fast_mode == 3) {
u64 startus;
startus = codec_mm_get_current_us();
codec_mm_page_free_to_cache_scatter(smgt, mms);
codec_mm_update_free_time(smgt, startus);
if (fast_mode == 2 || mms->page_used == mms->page_cnt) {
codec_mm_scatter_unlock(mms);
ATRACE_COUNTER("mmu_free",
MMU_FREE_SCATTER_START_LOCK_DONE);
return 0;
}
}
codec_mm_scatter_free_pages_in_locked(mms, start_free_id);
codec_mm_scatter_unlock(mms);
ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_LOCK_DONE);
return 0;
}
int codec_mm_scatter_free_tail_pages(
struct codec_mm_scatter *mms,
int start_id)
{
int ret = 0;
ret = codec_mm_scatter_free_tail_pages_in(
mms,
start_id, 0, 0);
return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_free_tail_pages);
int codec_mm_scatter_free_tail_pages_fast(
struct codec_mm_scatter *mms,
int start_free_id)
{
int ret = 0;
if (!mms)
return -1;
if (start_free_id < mms->page_cnt)
ret = codec_mm_scatter_free_tail_pages_in(
mms,
start_free_id, 2, 0);
codec_mm_schedule_delay_work(
(struct codec_mm_scatter_mgt *)mms->manager,
100, 0);
return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_free_tail_pages_fast);
int codec_mm_scatter_free_unused_pages(struct codec_mm_scatter *mms)
{
int ret = 0;
ret = codec_mm_scatter_free_tail_pages_in(mms,
0, 0, 1);
return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_free_unused_pages);
int codec_mm_scatter_less_pages(struct codec_mm_scatter *mms,
int nums)
{
int ret = 0;
ret = codec_mm_scatter_free_tail_pages_in(mms,
nums, 0, 2);
return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_less_pages);
/*free all pages only
*don't free scatter
*/
int codec_mm_scatter_free_all_pages(struct codec_mm_scatter *mms)
{
int ret;
ret = codec_mm_scatter_free_tail_pages(mms, 0);
return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_free_all_pages);
static inline int codec_mm_scatter_map_add_locked(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *mms)
{
int i;
for (i = 0; i < MAX_SC_LIST; i++) {
if (smgt->scmap[i] == NULL) {
smgt->scmap[i] = mms;
return i;
}
}
return -1;
}
static inline int codec_mm_scatter_map_del_locked(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *mms)
{
int i;
for (i = 0; i < MAX_SC_LIST; i++) {
if (smgt->scmap[i] == mms) {
smgt->scmap[i] = NULL;
return i;
}
}
return 0;
}
int codec_mm_scatter_valid_locked(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *mms)
{
int i;
int valid = 0;
for (i = 0; i < MAX_SC_LIST; i++) {
if (smgt->scmap[i] == mms) {
valid = 1;
break;
}
}
return valid;
}
EXPORT_SYMBOL(codec_mm_scatter_valid_locked);
/*free scatter's all */
static int codec_mm_scatter_free_on_nouser_ext(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *mms,
int not_in_mgt)
{
int ret = 0;
int free;
codec_mm_scatter_lock(mms);
codec_mm_list_lock(smgt);
ret = atomic_read(&mms->user_cnt);
if (ret > 0) {
codec_mm_list_unlock(smgt);
codec_mm_scatter_unlock(mms);
/*>0 have another user. */
/*pr_err("ERROR, scatter is not free.cnt:%d\n", ret); */
return 0;
}
/*to free now */
free = mms->page_cnt;
if (!list_empty(&mms->list))
list_del(&mms->list);
if (!not_in_mgt) {
smgt->scatters_cnt--;
codec_mm_scatter_map_del_locked(smgt, mms);
}
codec_mm_list_unlock(smgt);
codec_mm_scatter_unlock(mms);
if (mms->page_cnt > 0)
ret = codec_mm_scatter_free_tail_pages_in(mms, 0, 0, 0);
if (free >= 256 &&
(smgt->try_alloc_in_sys_page_cnt <
smgt->try_alloc_in_sys_page_cnt_max) &&
(smgt->total_page_num < (1024 * 1024 * 32 >> PAGE_SHIFT))) {
smgt->try_alloc_in_sys_page_cnt *= 2;
if (!smgt->support_from_slot_sys) {
smgt->support_from_slot_sys =
smgt->enable_slot_from_sys;
}
}
SC_FREE(mms);
return ret;
}
/*free scatter's all */
static int codec_mm_scatter_free_on_nouser(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *mms)
{
return codec_mm_scatter_free_on_nouser_ext(
smgt, mms, 0);
}
/*
*mask for other use it.
*/
static int codec_mm_scatter_inc_user_in1(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *mms,
int cnt)
{
int ret = -1;
int old_user;
codec_mm_list_lock(smgt);
if (!codec_mm_scatter_valid_locked(smgt, mms)) {
codec_mm_list_unlock(smgt);
return -1;
}
old_user = atomic_read(&mms->user_cnt);
if (old_user >= 0) {
ret = atomic_add_return(cnt, &mms->user_cnt);
if (old_user == 1)
mms->tofree_jiffies = jiffies;
}
codec_mm_list_unlock(smgt);
return ret <= 0 ? ret : 0; /*must add before user cnt >= 0 */
}
static int codec_mm_scatter_inc_user_in(
struct codec_mm_scatter *mms,
int cnt)
{
struct codec_mm_scatter_mgt *smgt;
int ret;
if (!mms)
return -1;
smgt = codec_mm_get_scatter_mgt(0);
ret = codec_mm_scatter_inc_user_in1(smgt,
mms,
cnt);
if (ret < 0) {
smgt = codec_mm_get_scatter_mgt(1);
ret = codec_mm_scatter_inc_user_in1(smgt,
mms,
cnt);
}
return ret;
}
/*mask scatter's to free.*/
static int codec_mm_scatter_dec_user_in1(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *mms,
int delay_free_ms, int cnt)
{
int after_users = 1;
codec_mm_list_lock(smgt);
if (!codec_mm_scatter_valid_locked(smgt, mms)) {
codec_mm_list_unlock(smgt);
return -1;
}
if (atomic_read(&mms->user_cnt) >= 1) {
after_users = atomic_sub_return(cnt, &mms->user_cnt);
if (after_users == 0) {
/*is free time*/
mms->tofree_jiffies = jiffies +
delay_free_ms * HZ/1000;
}
}
codec_mm_list_unlock(smgt);
if (after_users == 0)
codec_mm_schedule_delay_work(smgt, 0, 1);
return 0;
}
/*mask scatter's to free.*/
static int codec_mm_scatter_dec_user_in(
struct codec_mm_scatter *mms,
int delay_free_ms, int cnt)
{
struct codec_mm_scatter_mgt *smgt;
int ret;
if (!mms)
return -1;
smgt = codec_mm_get_scatter_mgt(0);
ret = codec_mm_scatter_dec_user_in1(smgt,
mms,
delay_free_ms,
cnt);
if (ret < 0) {
smgt = codec_mm_get_scatter_mgt(1);
ret = codec_mm_scatter_dec_user_in1(smgt,
mms,
delay_free_ms,
cnt);
}
return ret;
}
/*
*maybe a render/sink.video/osd/
*/
int codec_mm_scatter_inc_for_keeper(void *sc_mm)
{
struct codec_mm_scatter *mms = sc_mm;
return codec_mm_scatter_inc_user_in(mms, 100);
}
EXPORT_SYMBOL(codec_mm_scatter_inc_for_keeper);
/*
*maybe a render/sink.video/osd/
*/
int codec_mm_scatter_dec_keeper_user(void *sc_mm, int delay_ms)
{
struct codec_mm_scatter *mms = sc_mm;
return codec_mm_scatter_dec_user_in(mms, delay_ms, 100);
}
EXPORT_SYMBOL(codec_mm_scatter_dec_keeper_user);
int codec_mm_scatter_dec_owner_user(void *sc_mm, int delay_ms)
{
struct codec_mm_scatter *mms = sc_mm;
int ret = codec_mm_scatter_dec_user_in(mms, delay_ms, 1000);
if (ret)
ERR_LOG("dec_owner_user error %p\n", mms);
return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_dec_owner_user);
/*
*max pages:
*want pages now,
*maybe:
* max pages == support 4k,need pages;
* page num = current size need pages;
*/
struct codec_mm_scatter *codec_mm_scatter_alloc_new(
struct codec_mm_scatter_mgt *smgt,
int max_page,
int page_num)
{
struct codec_mm_scatter *mms;
int ret;
if (max_page < page_num)
return NULL;
mms = SC_ALLOC(sizeof(struct codec_mm_scatter) + sizeof(phy_addr_type) *
max_page, GFP_KERNEL);
if (!mms) {
ERR_LOG("no enough for mm scatter!!!!\n");
return NULL;
}
memset(mms, 0, sizeof(struct codec_mm_scatter));
mms->pages_list = (phy_addr_type *) (mms + 1);
mms->page_max_cnt = max_page;
INIT_LIST_HEAD(&mms->list);
memset(mms->pages_list, 0, sizeof(phy_addr_type) * max_page);
mms->page_cnt = 0;
mms->page_tail = -1;
mms->manager = (void *)smgt;
atomic_set(&mms->user_cnt, 0);
mutex_init(&mms->mutex);
if (page_num > 0) {
ret = codec_mm_page_alloc_all_locked(smgt,
mms->pages_list, page_num,
is_cache_sc(smgt, mms));
if (ret <= 0)
goto error;
mms->page_cnt = ret;
mms->page_tail = mms->page_cnt - 1;
}
atomic_set(&mms->user_cnt, 1000);
codec_mm_list_lock(smgt);
mms->page_used = mms->page_cnt;
list_add_tail(&mms->list, &smgt->scatter_list);
smgt->scatters_cnt++;
codec_mm_scatter_map_add_locked(smgt, mms);
codec_mm_list_unlock(smgt);
return mms;
error:
codec_mm_scatter_free_on_nouser_ext(smgt, mms, 1);
return NULL;
}
EXPORT_SYMBOL(codec_mm_scatter_alloc_new);
/*
*max pages:
*want pages now,
*maybe:
* max pages == support 4k,need pages;
* page num = current size need pages;
*/
struct codec_mm_scatter *codec_mm_scatter_alloc(
int max_page, int page_num,
int istvp)
{
struct codec_mm_scatter_mgt *smgt;
struct codec_mm_scatter *mms, *alloced_mms;
struct list_head *pos, *next;
int ret;
u64 startus;
smgt = codec_mm_get_scatter_mgt(istvp);
startus = codec_mm_get_current_us();
alloced_mms = NULL;
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_START);
codec_mm_list_lock(smgt);
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_START_LOCK);
if (!list_empty(&smgt->scatter_list)) { /*try find a free scatter. */
pos = smgt->scatter_list.prev; /*free on prev. */
while (pos != &smgt->scatter_list) {
next = pos->prev;
mms = list_entry(pos, struct codec_mm_scatter, list);
if (mms->page_max_cnt >= max_page &&
atomic_read(&mms->user_cnt) == 0) {
if (atomic_add_return(1000,
&mms->user_cnt) == 1000) {
mms->page_used = mms->page_cnt;
alloced_mms = mms;
break;
} else
atomic_sub(1000, &mms->user_cnt);
}
pos = next;
}
}
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_START_LOCK_DONE);
codec_mm_list_unlock(smgt);
if (!alloced_mms) {
/*
*just alloc mms first,
*alloc pages later.
*/
ATRACE_COUNTER("mmu alloc",
MMU_ALLOC_SCATTER_ALLOC_NEW);
alloced_mms = codec_mm_scatter_alloc_new(smgt, max_page, 0);
ATRACE_COUNTER("mmu alloc",
MMU_ALLOC_SCATTER_ALLOC_NEW_END);
}
if (alloced_mms) {
ATRACE_COUNTER("mmu alloc",
MMU_ALLOC_SCATTER_ALLOC_WANT_PAGE_IN);
ret = codec_mm_scatter_alloc_want_pages_in(smgt, alloced_mms,
page_num);
ATRACE_COUNTER("mmu alloc",
MMU_ALLOC_SCATTER_ALLOC_WANT_PAGE_IN_END);
if (ret < 0) {
atomic_sub(1000, &alloced_mms->user_cnt);
return NULL;
}
/*pr_info("reused old mms! %p\n", alloced_mms);*/
codec_mm_update_alloc_time(smgt, startus);
return alloced_mms;
}
return NULL;
}
EXPORT_SYMBOL(codec_mm_scatter_alloc);
static int codec_mm_scatter_alloc_want_pages_in(
struct codec_mm_scatter_mgt *smgt,
struct codec_mm_scatter *mms,
int want_pages)
{
int ret = 0;
if (want_pages > mms->page_max_cnt)
return CODEC_MM_S_ERR(100);
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_LOCK);
codec_mm_scatter_lock(mms);
codec_mm_list_lock(smgt);
mms->page_used = want_pages;
codec_mm_list_unlock(smgt);
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_LOCK_END);
if (want_pages > mms->page_cnt) {
ret = codec_mm_page_alloc_all_locked(
smgt,
&mms->pages_list[mms->page_tail + 1],
want_pages - mms->page_cnt,
is_cache_sc(smgt, mms));
if (ret <= 0) {
codec_mm_scatter_unlock(mms);
ERR_LOG("can't alloc want pages %d\n", want_pages);
return ret;
}
mms->page_cnt += ret;
mms->page_tail += ret;
}
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_LIST_LOCK_START);
codec_mm_list_lock(smgt);
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_LIST_LOCK);
if (ret > 0 && is_cache_sc(smgt, mms))
smgt->cached_pages += ret;
codec_mm_list_unlock(smgt);
ATRACE_COUNTER("mmu alloc", MMU_ALLOC_LIST_LOCK_END);
codec_mm_scatter_unlock(mms);
if (smgt->cached_pages < smgt->keep_size_PAGE / 2) {
/*try alloc more cache.*/
codec_mm_schedule_delay_work(smgt, 0, 1);
}
return 0;
}
int codec_mm_scatter_alloc_want_pages(
struct codec_mm_scatter *mms,
int want_pages)
{
struct codec_mm_scatter_mgt *smgt;
int ret;
u64 startus;
if (!mms)
return -1;
smgt = (struct codec_mm_scatter_mgt *)mms->manager;
startus = codec_mm_get_current_us();
ret = codec_mm_scatter_alloc_want_pages_in(
smgt, mms, want_pages);
codec_mm_update_alloc_time(smgt, startus);
return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_alloc_want_pages);
int codec_mm_free_all_free_slots_in(struct codec_mm_scatter_mgt *smgt)
{
struct codec_mm_slot *slot, *to_free;
do {
to_free = NULL;
codec_mm_list_lock(smgt);
{
struct list_head *header, *list;
header = &smgt->free_list;
list = header->prev;
while (list != header) {
slot = list_entry(list, struct codec_mm_slot,
free_list);
if (slot->alloced_page_num == 0) {
list_del_init(&slot->free_list);
to_free = slot;
break;
}
list = list->prev;
};
}
codec_mm_list_unlock(smgt);
if (!to_free)
break;
codec_mm_slot_free(smgt, to_free);
} while (1);
return 0;
}
EXPORT_SYMBOL(codec_mm_free_all_free_slots);
int codec_mm_free_all_free_slots(void)
{
codec_mm_free_all_free_slots_in(codec_mm_get_scatter_mgt(0));
codec_mm_free_all_free_slots_in(codec_mm_get_scatter_mgt(1));
return 0;
}
static int codec_mm_scatter_info_dump_in(
struct codec_mm_scatter_mgt *smgt,
void *buf, int size)
{
char *pbuf = buf;
char sbuf[512];
int tsize = 0;
int s;
int n;
if (!pbuf)
pbuf = sbuf;
#define BUFPRINT(args...) \
do {\
s = sprintf(pbuf, args);\
tsize += s;\
pbuf += s; \
} while (0)
BUFPRINT("codec %sscattered memory info:\n",
smgt->tvp_mode ? "TVP " : "");
BUFPRINT("\ttotal size:%dM, %d Bytes,pages:%d\n",
(smgt->total_page_num << PAGE_SHIFT) / SZ_1M,
smgt->total_page_num << PAGE_SHIFT,
smgt->total_page_num);
n = smgt->alloced_page_num;
BUFPRINT("\talloced size:%dM, %d Bypes,pages:%d\n",
(n << PAGE_SHIFT) / SZ_1M,
n << PAGE_SHIFT,
n);
BUFPRINT("\tmax alloced:%d M | %d pages\n",
(smgt->max_alloced << PAGE_SHIFT) / SZ_1M,
smgt->max_alloced);
BUFPRINT("\tscatter cached:%d M |%d pages\n",
(smgt->cached_pages << PAGE_SHIFT) / SZ_1M,
smgt->cached_pages);
BUFPRINT("\talloc from sys size:%d\n",
(smgt->alloc_from_sys_sc_cnt +
smgt->one_page_cnt) << PAGE_SHIFT);
BUFPRINT("\talloc from sys sc cnt:%d\n",
smgt->alloc_from_sys_sc_cnt);
BUFPRINT("\talloc from sys pages cnt:%d pages\n",
smgt->alloc_from_sys_page_cnt);
BUFPRINT("\talloc from sys max pages cnt:%d pages\n",
smgt->alloc_from_sys_max_page_cnt);
BUFPRINT("\tscatter_task_run:%d\n",
smgt->scatter_task_run_num);
BUFPRINT("\tone_page_cnt:%d\n",
smgt->one_page_cnt);
BUFPRINT("\tcatters cnt:%d\n", smgt->scatters_cnt);
BUFPRINT("\tslot cnt:%d\n", smgt->slot_cnt);
BUFPRINT("\tcma alloc block size:%d\n",
smgt->try_alloc_in_cma_page_cnt);
BUFPRINT("\tsys alloc block size:%d\n",
smgt->try_alloc_in_sys_page_cnt);
BUFPRINT("\tdelay_free_on:%d\n",
smgt->delay_free_on);
BUFPRINT("\tdelay_free_on time:%lld jiffies\n",
smgt->delay_free_timeout_jiffies64);
BUFPRINT("\tcurrent time:%lld\n",
get_jiffies_64());
BUFPRINT("\talloc time max us:%d\n",
smgt->alloc_max_us);
BUFPRINT("\talloc cnt:%d step:%d:%d:%d:%d:%d:%d:%d\n",
smgt->alloc_cnt,
smgt->alloc_10us_less_cnt,
smgt->alloc_10_50us_cnt,
smgt->alloc_50_100us_cnt,
smgt->alloc_100_1000us_cnt,
smgt->alloc_1_10ms_cnt,
smgt->alloc_10_100ms_cnt,
smgt->alloc_100ms_up_cnt
);
BUFPRINT("\tfree time max us:%d\n",
smgt->free_max_us);
BUFPRINT("\tfree cnt:%d step:%d:%d:%d:%d:%d:%d:%d\n",
smgt->free_cnt,
smgt->free_10us_less_cnt,
smgt->free_10_50us_cnt,
smgt->free_50_100us_cnt,
smgt->free_100_1000us_cnt,
smgt->free_1_10ms_cnt,
smgt->free_10_100ms_cnt,
smgt->free_100ms_up_cnt
);
{
int average_timeus;
u64 divider = smgt->alloc_total_us;
do_div(divider, smgt->alloc_cnt);
average_timeus = (smgt->alloc_cnt == 0 ?
0 : (int)divider);
BUFPRINT("\talloc time average us:%d\n",
average_timeus);
divider = smgt->free_total_us;
do_div(divider, smgt->free_cnt);
average_timeus = (smgt->free_cnt == 0 ?
0 : (int)divider);
BUFPRINT("\tfree time average us:%d\n",
average_timeus);
}
#undef BUFPRINT
if (!buf)
INFO_LOG("%s", sbuf);
return tsize;
}
EXPORT_SYMBOL(codec_mm_scatter_info_dump);
int codec_mm_scatter_info_dump(
void *buf, int size)
{
char *pbuf = buf;
int esize = size;
int ret;
ret = codec_mm_scatter_info_dump_in(
codec_mm_get_scatter_mgt(0),
pbuf,
esize);
if (buf != NULL && ret > 0) {
pbuf += ret;
esize -= ret;
}
ret += codec_mm_scatter_info_dump_in(
codec_mm_get_scatter_mgt(1),
pbuf,
esize);
return ret;
}
int codec_mm_dump_slot(struct codec_mm_slot *slot, void *buf, int size)
{
char *pbuf = buf;
char sbuf[512];
int tsize = 0;
int s;
int i;
int sum;
char bits_incharNhalf[] = { 0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 2, 3, 4, 1, 2};
if (!pbuf)
pbuf = sbuf;
#define BUFPRINT(args...) \
do {\
s = sprintf(pbuf, args);\
tsize += s;\
pbuf += s; \
} while (0)
BUFPRINT("slot info:%p\n", slot);
BUFPRINT("\tfrom:%s\n",
(slot->from_type == SLOT_FROM_CODEC_MM ? "codec_mm" :
"sys"));
BUFPRINT("\tbase addr range:%p<-->%p\n",
(void *)slot->phy_addr,
(void *)(slot->phy_addr +
(slot->page_num << PAGE_SHIFT)
- 1));
BUFPRINT("\tpage_num:%d\n", slot->page_num);
BUFPRINT("\talloced:%d,free:%d\n", slot->alloced_page_num,
slot->page_num - slot->alloced_page_num);
BUFPRINT("\tnext bit:%d\n", slot->next_bit);
BUFPRINT("\tsid:%x\n", slot->sid);
BUFPRINT("\troot:%d\n", slot->isroot);
sum = 0;
for (i = 0; i < slot->pagemap_size; i++) {
int c = ((unsigned char *)slot->pagemap)[i];
sum += bits_incharNhalf[c & 0xf] + bits_incharNhalf[c >> 4];
}
BUFPRINT("\tbitmap.setbits.sum:%d\n", sum);
BUFPRINT("\tbitmap");
i = 0;
while (i < slot->pagemap_size && i < 16)
BUFPRINT(":%02x", ((char *)slot->pagemap)[i++]);
BUFPRINT("\n");
#undef BUFPRINT
if (!buf)
INFO_LOG("%s", sbuf);
return 0;
}
EXPORT_SYMBOL(codec_mm_dump_slot);
int codec_mm_dump_all_slots_in(struct codec_mm_scatter_mgt *smgt)
{
struct codec_mm_slot *slot, *fslot;
int total_pages = 0;
int alloced_pages = 0;
int slot_cnt = 0;
int i;
codec_mm_list_lock(smgt);
INFO_LOG("start dump all slots!\n");
for (i = 0; i < MAX_SID; i++) {
fslot = smgt->slot_list_map[i];
if (fslot) {
codec_mm_dump_slot(fslot, NULL, 0);
slot_cnt++;
total_pages += fslot->page_num;
alloced_pages += fslot->alloced_page_num;
if (!list_empty(&fslot->sid_list)) {
slot = list_entry(fslot->sid_list.next,
struct codec_mm_slot, sid_list);
while (slot != fslot) {
codec_mm_dump_slot(slot, NULL, 0);
slot_cnt++;
total_pages += slot->page_num;
alloced_pages += slot->alloced_page_num;
slot = list_entry(slot->sid_list.next,
struct codec_mm_slot, sid_list);
}
}
}
}
codec_mm_list_unlock(smgt);
INFO_LOG("end dump, slot cnt:%d total pages:%d, free:%d\n",
slot_cnt,
total_pages,
total_pages - alloced_pages);
return 0;
}
EXPORT_SYMBOL(codec_mm_dump_all_slots);
int codec_mm_dump_all_slots(void)
{
codec_mm_dump_all_slots_in(codec_mm_get_scatter_mgt(0));
codec_mm_dump_all_slots_in(codec_mm_get_scatter_mgt(1));
return 0;
}
int codec_mm_dump_all_hash_table_in(
struct codec_mm_scatter_mgt *smgt)
{
struct codec_mm_slot *slot, *fslot;
int i;
int total_pages = 0;
int alloced_pages = 0;
INFO_LOG("start dump sid hash table!\n");
codec_mm_list_lock(smgt);
for (i = 0; i < MAX_SID; i++) {
int cnt = 0;
int pages = 0;
int alloced = 0;
fslot = smgt->slot_list_map[i];
if (fslot) {
cnt++;
pages += fslot->page_num;
alloced += fslot->alloced_page_num;
if (!list_empty(&fslot->sid_list)) {
slot = list_entry(fslot->sid_list.next,
struct codec_mm_slot, sid_list);
while (slot != fslot) {
cnt++;
pages += slot->page_num;
alloced += slot->alloced_page_num;
slot = list_entry(slot->sid_list.next,
struct codec_mm_slot, sid_list);
}
}
}
if (cnt > 0) {
total_pages += pages;
alloced_pages += alloced;
INFO_LOG(
"\tSID(%d):\tslots:%d,\tpages:%d:\tfree pages:%d\n",
i, cnt, pages, pages - alloced);
}
}
codec_mm_list_unlock(smgt);
INFO_LOG("end dump sid hash table, total pages:%d, free:%d\n",
total_pages, total_pages - alloced_pages);
return 0;
}
EXPORT_SYMBOL(codec_mm_dump_all_hash_table);
int codec_mm_dump_all_hash_table(void)
{
codec_mm_dump_all_hash_table_in(codec_mm_get_scatter_mgt(0));
codec_mm_dump_all_hash_table_in(codec_mm_get_scatter_mgt(1));
return 0;
}
static int codec_mm_dump_free_slots_in(struct codec_mm_scatter_mgt *smgt)
{
struct codec_mm_slot *slot;
int total_pages = 0;
int alloced_pages = 0;
INFO_LOG("dump all free slots:\n");
codec_mm_list_lock(smgt);
if (!list_empty(&smgt->free_list)) {
struct list_head *header, *list;
header = &smgt->free_list;
list = header->prev;
while (list != header) {
slot = list_entry(list, struct codec_mm_slot,
free_list);
codec_mm_dump_slot(slot, NULL, 0);
total_pages += slot->page_num;
alloced_pages += slot->alloced_page_num;
list = list->prev;
};
}
codec_mm_list_unlock(smgt);
INFO_LOG("end all free slots: total pages:%d, freed:%d\n",
total_pages,
total_pages - alloced_pages);
return 0;
}
EXPORT_SYMBOL(codec_mm_dump_free_slots);
int codec_mm_dump_free_slots(void)
{
codec_mm_dump_free_slots_in(codec_mm_get_scatter_mgt(0));
codec_mm_dump_free_slots_in(codec_mm_get_scatter_mgt(1));
return 0;
}
int codec_mm_dump_scatter(
struct codec_mm_scatter *mms,
void *buf, int size)
{
char *pbuf = buf;
char sbuf[512];
int tsize = 0;
int s;
int i;
if (!pbuf)
pbuf = sbuf;
#define BUFPRINT(args...) \
do {\
s = sprintf(pbuf, args);\
tsize += s;\
pbuf += s; \
} while (0)
BUFPRINT("scatter info:%p\n", mms);
BUFPRINT("\tsize:%d\n", (int)(mms->page_cnt * PAGE_SIZE));
BUFPRINT("\tmax:%d\n", mms->page_max_cnt);
BUFPRINT("\tpage_cnt:%d\n", mms->page_cnt);
BUFPRINT("\tpage_used:%d\n", mms->page_used);
BUFPRINT("\tpage_tail:%d\n", mms->page_tail);
BUFPRINT("\tuser_cnt:%d\n", atomic_read(&mms->user_cnt));
BUFPRINT("\ttofree_jiffies:%ld\n", mms->tofree_jiffies);
i = 0;
while (i < mms->page_cnt && i < 16)
BUFPRINT(":%x", (u32) mms->pages_list[i++]);
BUFPRINT("\n");
#undef BUFPRINT
if (!buf)
INFO_LOG("%s", sbuf);
return 0;
}
EXPORT_SYMBOL(codec_mm_dump_scatter);
static int codec_mm_dump_all_scatters_in(struct codec_mm_scatter_mgt *smgt)
{
struct codec_mm_scatter *mms;
struct list_head *pos, *tmp;
INFO_LOG("start dump all scatters!\n");
codec_mm_list_lock(smgt);
do {
if (list_empty(&smgt->scatter_list))
break;
list_for_each_safe(pos, tmp, &smgt->scatter_list) {
mms = list_entry(pos, struct codec_mm_scatter, list);
codec_mm_dump_scatter(mms, 0, 0);
}
} while (0);
INFO_LOG("start dump free scatters!\n");
if (smgt->cache_scs[0])
codec_mm_dump_scatter(smgt->cache_scs[0], 0, 0);
if (smgt->cache_scs[1])
codec_mm_dump_scatter(smgt->cache_scs[1], 0, 0);
codec_mm_list_unlock(smgt);
INFO_LOG("finished dump all scatters!\n");
return 0;
}
EXPORT_SYMBOL(codec_mm_dump_all_scatters);
int codec_mm_dump_all_scatters(void)
{
codec_mm_dump_all_scatters_in(codec_mm_get_scatter_mgt(0));
codec_mm_dump_all_scatters_in(codec_mm_get_scatter_mgt(1));
return 0;
}
int codec_mm_scatter_update_config(struct codec_mm_scatter_mgt *smgt)
{
smgt->keep_size_PAGE = g_scatter.keep_size_PAGE;
smgt->reserved_block_mm_M = g_scatter.reserved_block_mm_M;
smgt->try_alloc_in_cma_page_cnt = g_scatter.try_alloc_in_cma_page_cnt;
smgt->try_alloc_in_sys_page_cnt_max
= g_scatter.try_alloc_in_sys_page_cnt_max;
smgt->try_alloc_in_sys_page_cnt_min
= g_scatter.try_alloc_in_sys_page_cnt_min;
smgt->enable_slot_from_sys = g_scatter.enable_slot_from_sys;
smgt->support_from_slot_sys = g_scatter.support_from_slot_sys;
smgt->no_cache_size_M = g_scatter.no_cache_size_M;
smgt->no_alloc_from_sys = g_scatter.no_alloc_from_sys;
return 0;
}
int codec_mm_scatter_size(int is_tvp)
{
struct codec_mm_scatter_mgt *smgt;
smgt = codec_mm_get_scatter_mgt(is_tvp ? 1 : 0);
return smgt->total_page_num;
}
EXPORT_SYMBOL(codec_mm_scatter_size);
int codec_mm_scatter_mgt_delay_free_swith(
int on,
int delay_ms,
int wait_size_M,
int is_tvp)
{
struct codec_mm_scatter_mgt *smgt;
smgt = codec_mm_get_scatter_mgt(is_tvp);
codec_mm_list_lock(smgt);
if (on) {
smgt->delay_free_on++;
smgt->delay_free_timeout_jiffies64 =
get_jiffies_64() + delay_ms * HZ/1000;
} else {
smgt->delay_free_on--;
if (smgt->delay_free_on <= 0) {
smgt->delay_free_on = 0;
smgt->delay_free_timeout_jiffies64 =
get_jiffies_64() + delay_ms * HZ/1000;
}
}
codec_mm_list_unlock(smgt);
if (on && wait_size_M > 0 /*&& !is_tvp*/) {
smgt->force_cache_on = 1;
smgt->force_cache_page_cnt = wait_size_M >> PAGE_SHIFT;
smgt->delay_free_timeout_jiffies64 =
get_jiffies_64() + 10000 * HZ/1000;
codec_mm_schedule_delay_work(smgt, 0, 1);/*start cache*/
#if 0
while (smgt->total_page_num < smgt->force_cache_page_cnt) {
if (smgt->cache_scs[0] &&
(smgt->cached_pages >= 65000)) {
/*cache sc fulled.*/
break;
}
if (try_max-- <= 0 || time_after64(get_jiffies_64(),
start_time + HZ)) {
break;
}
ret = wait_for_completion_timeout(
&smgt->complete,
HZ/10);
if (ret == 0)
pr_debug("codec_mm_scatter_mgt_delay_free_swith time out\n");
}
pr_info("end: cached pages: %d, speed %d ms\n",
smgt->cached_pages,
(int)(get_jiffies_64() - start_time) * 1000/HZ);
smgt->force_cache_on = 0;
smgt->delay_free_timeout_jiffies64 =
get_jiffies_64() + delay_ms * HZ/1000;
#endif
} else if (on) {
codec_mm_schedule_delay_work(smgt, 0, 1);
} else {
codec_mm_schedule_delay_work(smgt, delay_ms, 0);
}
return 0;
}
EXPORT_SYMBOL(codec_mm_scatter_mgt_delay_free_swith);
static void codec_mm_scatter_cache_manage(
struct codec_mm_scatter_mgt *smgt)
{
struct codec_mm_scatter *mms;
int alloced = 0;
int total_free_page = smgt->total_page_num -
smgt->alloced_page_num + smgt->cached_pages;
if (smgt->delay_free_on > 0 && smgt->keep_size_PAGE > 0) {
/*if alloc too much ,don't cache any more.*/
if (smgt->no_cache_size_M > 0 &&
(smgt->cached_pages <= smgt->keep_size_PAGE) &&
(smgt->total_page_num >=
(smgt->no_cache_size_M * (SZ_1M >> PAGE_SHIFT)))) {
/*have enough pages for most movies.*/
/*don't cache more.*/
if (smgt->force_cache_on) {
smgt->force_cache_on = 0;
smgt->delay_free_timeout_jiffies64 =
get_jiffies_64() + 2000 * HZ/1000;
}
} else if ((smgt->cached_pages < smgt->keep_size_PAGE) ||
(smgt->force_cache_on &&/*on star cache*/
(smgt->total_page_num < smgt->force_cache_page_cnt))
) {/*first 500ms ,alloc double.*/
mms = codec_mm_get_next_cache_scatter(smgt, NULL, 0);
if (mms) {
int need;
int once_alloc = 1000;/*once 4M*/
if (smgt->force_cache_on) {
once_alloc = 4000;
if ((smgt->total_page_num -
smgt->alloced_page_num) >
once_alloc) {
once_alloc =
smgt->total_page_num -
smgt->alloced_page_num;
}
}
need = mms->page_cnt + once_alloc;
if ((need - mms->page_cnt) > once_alloc)
need = mms->page_cnt + once_alloc;
if (need > smgt->force_cache_page_cnt)
need = smgt->force_cache_page_cnt;
if (need > mms->page_max_cnt)
need = mms->page_max_cnt - 4;
if (need > mms->page_cnt) {
ATRACE_COUNTER("mmu alloc",
MMU_ALLOC_SCATTER_ALLOC_WANT_PAGE_IN_2);
alloced =
!codec_mm_scatter_alloc_want_pages_in(
smgt,
mms,
need);
ATRACE_COUNTER("mmu alloc",
MMU_ALLOC_SCATTER_ALLOC_WANT_PAGE_IN_2_END);
} else {
alloced = 0;
}
} else {
alloced = 0;
}
/*wake up wait.*/
if (alloced &&
smgt->force_cache_on &&
(smgt->cached_pages >=
smgt->force_cache_page_cnt)) {
smgt->force_cache_on = 0;
smgt->delay_free_timeout_jiffies64 =
get_jiffies_64() + 2000 * HZ/1000;
}
} else if ((smgt->cached_pages >
(smgt->keep_size_PAGE + 1000)) &&
time_after64(get_jiffies_64(),
smgt->delay_free_timeout_jiffies64)) {
/*wait time out can free.*/
mms = codec_mm_get_next_cache_scatter(smgt, NULL, 0);
if (mms) {/*only free some 1M cache*/
codec_mm_scatter_less_pages(mms,
256);
}
codec_mm_free_all_free_slots_in(smgt);
/*free some slots.*/
}
} else if (smgt->delay_free_on <= 0 &&
time_after64(get_jiffies_64(),
smgt->delay_free_timeout_jiffies64)) {
/*free all free pages, no delay needed.*/
codec_mm_free_all_free_slots_in(smgt);
}
if (smgt->keep_size_PAGE > 0 && smgt->delay_free_on) {
if (((smgt->force_cache_on ||
(total_free_page < smgt->keep_size_PAGE)) /*&&*/
/*!smgt->tvp_mode*/) &&
alloced) {/*if failed may deadlock...*/
/*ignore keep on tvp mode.*/
if (smgt->force_cache_on && !smgt->tvp_mode)
codec_mm_schedule_delay_work(smgt, 0, 1);
else
codec_mm_schedule_delay_work(smgt, 10, 0);
} else
codec_mm_schedule_delay_work(smgt, 100, 0);
} else if (!smgt->delay_free_on && smgt->total_page_num > 0) {
codec_mm_schedule_delay_work(smgt, 100, 0);
}
}
static int codec_mm_scatter_scatter_arrange(
struct codec_mm_scatter_mgt *smgt)
{
struct codec_mm_scatter *mms;
struct codec_mm_scatter *first_free_mms = NULL;
struct list_head *pos, *tmp;
int n = 0;
if (smgt->delay_free_on > 0 && !smgt->cache_scs[0]) {
/*cache1*/
mms = codec_mm_scatter_alloc_new(smgt, 16384, 0);
if (mms) {
codec_mm_list_lock(smgt);
list_del_init(&mms->list);
smgt->cache_scs[0] = mms;
codec_mm_list_unlock(smgt);
}
/*cache2*/
mms = codec_mm_scatter_alloc_new(smgt, 16384, 0);
if (mms) {
codec_mm_list_lock(smgt);
list_del_init(&mms->list);
smgt->cache_scs[1] = mms;
codec_mm_list_unlock(smgt);
}
}
if (smgt->delay_free_on <= 0 && smgt->cache_scs[0] &&
time_after64(get_jiffies_64(),
smgt->delay_free_timeout_jiffies64)) {
struct codec_mm_scatter *mms0, *mms1;
codec_mm_list_lock(smgt);
mms0 = smgt->cache_scs[0];
mms1 = smgt->cache_scs[1];
smgt->cache_scs[0] = NULL;
smgt->cache_scs[1] = NULL;
smgt->cached_pages = 0;
codec_mm_list_unlock(smgt);
if (mms0) {
codec_mm_scatter_dec_owner_user(mms0, 0);
codec_mm_scatter_free_on_nouser(smgt, mms0);
}
if (mms1) {
codec_mm_scatter_dec_owner_user(mms1, 0);
codec_mm_scatter_free_on_nouser(smgt, mms1);
}
}
codec_mm_list_lock(smgt);
if (list_empty(&smgt->scatter_list)) {
codec_mm_list_unlock(smgt);
return 0;
}
list_for_each_safe(pos, tmp, &smgt->scatter_list) {
mms = list_entry(pos, struct codec_mm_scatter, list);
if (atomic_read(&mms->user_cnt) == 0 &&
time_after(jiffies, mms->tofree_jiffies)) {
if (!first_free_mms ||
mms->tofree_jiffies <
first_free_mms->tofree_jiffies)
first_free_mms = mms;
} else
n++;
}
if (first_free_mms)
list_move_tail(&mms->list, &smgt->scatter_list);
codec_mm_list_unlock(smgt);
return 0;
}
static int codec_mm_scatter_scatter_clear(
struct codec_mm_scatter_mgt *smgt,
int force)/*not check jiffies & cache*/
{
struct codec_mm_scatter *mms, *to_free_mms, *less_page_mms;
struct list_head *pos, *tmp;
int to_free_mms_cnt = 0;
codec_mm_list_lock(smgt);
to_free_mms = NULL;
less_page_mms = NULL;
list_for_each_safe(pos, tmp, &smgt->scatter_list) {
mms = list_entry(pos, struct codec_mm_scatter, list);
if (atomic_read(&mms->user_cnt) == 0) {
if (!to_free_mms ||
(mms->tofree_jiffies <
to_free_mms->tofree_jiffies))
to_free_mms = mms;
to_free_mms_cnt++;
}
if (!less_page_mms && mms->page_used < mms->page_cnt) {
less_page_mms = mms;
break;
}
}
if ((to_free_mms != NULL) &&
(((to_free_mms_cnt > 1 || !smgt->delay_free_on) &&
time_after(jiffies, to_free_mms->tofree_jiffies)) ||
force)) { /*force== no checktimeer. */
/*set to nagative for free now. */
int cnt = atomic_sub_return(100000, &to_free_mms->user_cnt);
if (cnt != -100000) {
atomic_add(100000, &to_free_mms->user_cnt);
to_free_mms = NULL;
} else {
list_del_init(&to_free_mms->list);
}
} else {
to_free_mms = NULL;
}
codec_mm_list_unlock(smgt);
if (to_free_mms != NULL)
codec_mm_scatter_free_on_nouser(smgt, to_free_mms);
if (less_page_mms && (less_page_mms != to_free_mms))
codec_mm_scatter_free_unused_pages(less_page_mms);
return (to_free_mms != NULL) || (less_page_mms != NULL);
}
/*
*clear all ignore any cache.
*
*return the total num alloced.
*0 is all freeed.
*N is have some pages not alloced.
*/
static int codec_mm_scatter_free_all_ignorecache_in(
struct codec_mm_scatter_mgt *smgt)
{
int need_retry = 1;
int retry_num = 0;
mutex_lock(&smgt->monitor_lock);
pr_info("force free all scatter ignorecache!\n");
do {
struct codec_mm_scatter *mms;
/*clear cache first. */
/*disabled free on first. */
smgt->delay_free_on = 0;
codec_mm_list_lock(smgt);
mms = smgt->cache_scs[0];
smgt->cache_scs[0] = smgt->cache_scs[1];
smgt->cache_scs[1] = NULL;
smgt->cached_pages = 0;
codec_mm_list_unlock(smgt);
if (mms) {
pr_info("cache_sc page_max %d, page_used %d\n",
mms->page_max_cnt, mms->page_used);
codec_mm_scatter_dec_owner_user(mms, 0);
codec_mm_scatter_free_on_nouser(smgt, mms);
}
/*alloced again on timer?*/
/* check again. */
} while (smgt->cache_scs[0] != NULL);
do {
need_retry = codec_mm_scatter_scatter_clear(smgt, 1);
} while ((smgt->scatters_cnt > 0) && (retry_num++ < 1000));
if (need_retry || smgt->scatters_cnt > 0) {
pr_info("can't free all scatter, because some have used!!\n");
/*codec_mm_dump_all_scatters();*/
}
codec_mm_free_all_free_slots_in(smgt);
if (smgt->total_page_num > 0) {
/*have some not free,dump tables for debug */
pr_info("Some slots have not free!!\n\n");
/*codec_mm_dump_all_hash_table();*/
}
mutex_unlock(&smgt->monitor_lock);
return smgt->total_page_num;
}
EXPORT_SYMBOL(codec_mm_scatter_free_all_ignorecache);
int codec_mm_scatter_free_all_ignorecache(int flags)
{
if (flags & 1)
codec_mm_scatter_free_all_ignorecache_in(
codec_mm_get_scatter_mgt(0));
if (flags & 2)
codec_mm_scatter_free_all_ignorecache_in(
codec_mm_get_scatter_mgt(1));
return 0;
}
static void codec_mm_scatter_monitor(struct work_struct *work)
{
struct codec_mm_scatter_mgt *smgt = container_of(work,
struct codec_mm_scatter_mgt,
dealy_work.work);
int needretry = 0;
codec_mm_scatter_update_config(smgt);
mutex_lock(&smgt->monitor_lock);
smgt->scatter_task_run_num++;
codec_mm_scatter_scatter_arrange(smgt);
needretry = codec_mm_scatter_scatter_clear(smgt, 0);
if (needretry)
codec_mm_schedule_delay_work(smgt, 10, 0);
else if (smgt->scatters_cnt > 0)
codec_mm_schedule_delay_work(smgt, 100, 0);
codec_mm_scatter_cache_manage(smgt);
mutex_unlock(&smgt->monitor_lock);
}
static int codec_mm_scatter_mgt_alloc_in(struct codec_mm_scatter_mgt **psmgt)
{
struct codec_mm_scatter_mgt *smgt;
smgt = kmalloc(sizeof(struct codec_mm_scatter_mgt), GFP_KERNEL);
if (!smgt) {
ERR_LOG("ERR:codec mm mpt init ERROR\n");
return -1;
}
memset(smgt, 0, sizeof(struct codec_mm_scatter_mgt));
spin_lock_init(&smgt->list_lock);
smgt->tag = SMGT_IDENTIFY_TAG;
smgt->alloced_page_num = 0;
smgt->try_alloc_in_cma_page_cnt = (16 * 1024 * 1024) / PAGE_SIZE;
smgt->try_alloc_in_sys_page_cnt_max = MAX_SYS_BLOCK_PAGE;
smgt->try_alloc_in_sys_page_cnt = MAX_SYS_BLOCK_PAGE;
smgt->try_alloc_in_sys_page_cnt_min = MIN_SYS_BLOCK_PAGE;
smgt->reserved_block_mm_M = 128;
smgt->keep_size_PAGE = 20 * SZ_1M >> PAGE_SHIFT;
smgt->alloc_from_cma_first = 1;
smgt->enable_slot_from_sys = 0;
smgt->support_from_slot_sys =
smgt->enable_slot_from_sys;
smgt->mem_flags = CODEC_MM_FLAGS_CMA_FIRST |
CODEC_MM_FLAGS_FOR_VDECODER |
CODEC_MM_FLAGS_FOR_SCATTER;
if ((totalram_pages << PAGE_SHIFT) < 800 * SZ_1M) {
/*less memory boards don't cache more,*/
/*after alloced many pages.*/
smgt->no_cache_size_M = 100;
} else
smgt->no_cache_size_M = 0;
init_completion(&smgt->complete);
INIT_LIST_HEAD(&smgt->free_list);
INIT_LIST_HEAD(&smgt->scatter_list);
mutex_init(&smgt->monitor_lock);
INIT_DELAYED_WORK(&smgt->dealy_work,
codec_mm_scatter_monitor);
*psmgt = smgt;
return 0;
}
static struct mconfig codec_mm_sc_configs[] = {
MC_PU32("keep_size_PAGE", &g_scatter.keep_size_PAGE),
MC_PU32("reserved_block_mm_M", &g_scatter.reserved_block_mm_M),
MC_PU32("try_alloc_in_cma_page_cnt",
&g_scatter.try_alloc_in_cma_page_cnt),
MC_PU32("try_alloc_in_sys_page_cnt_max",
&g_scatter.try_alloc_in_sys_page_cnt_max),
MC_PU32("try_alloc_in_sys_page_cnt_min",
&g_scatter.try_alloc_in_sys_page_cnt_min),
MC_PU32("enable_slot_from_sys",
&g_scatter.enable_slot_from_sys),
MC_PU32("no_cache_size_M", &g_scatter.no_cache_size_M),
MC_PU32("no_alloc_from_sys", &g_scatter.no_alloc_from_sys),
};
static struct mconfig_node codec_mm_sc;
int codec_mm_scatter_mgt_init(void)
{
struct codec_mm_scatter_mgt *smgt;
codec_mm_scatter_mgt_alloc_in(&scatter_mgt);
codec_mm_scatter_mgt_alloc_in(&scatter_tvp_mgt);
scatter_tvp_mgt->tvp_mode = 1;
scatter_tvp_mgt->mem_flags |= CODEC_MM_FLAGS_TVP;
smgt = scatter_mgt;
g_scatter.keep_size_PAGE = smgt->keep_size_PAGE;
g_scatter.reserved_block_mm_M = smgt->reserved_block_mm_M;
g_scatter.try_alloc_in_cma_page_cnt = smgt->try_alloc_in_cma_page_cnt;
g_scatter.try_alloc_in_sys_page_cnt_max
= smgt->try_alloc_in_sys_page_cnt_max;
g_scatter.try_alloc_in_sys_page_cnt_min
= smgt->try_alloc_in_sys_page_cnt_min;
g_scatter.enable_slot_from_sys = smgt->enable_slot_from_sys;
g_scatter.support_from_slot_sys = smgt->support_from_slot_sys;
g_scatter.no_cache_size_M = smgt->no_cache_size_M;
g_scatter.no_alloc_from_sys = 0;
INIT_REG_NODE_CONFIGS("media.codec_mm",
&codec_mm_sc, "scatter",
codec_mm_sc_configs,
CONFIG_FOR_RW);
return 0;
}
int codec_mm_scatter_mgt_test(void)
{
#if 0
struct codec_mm_scatter *sc[64];
INFO_LOG("codec_mm_scatter_mgt_test end dump info.11..\n");
codec_mm_scatter_info_dump(NULL, 0);
sc[0] = codec_mm_scatter_alloc(10240, 10000);
sc[1] = codec_mm_scatter_alloc(10240, 10000);
sc[2] = codec_mm_scatter_alloc(10240, 10000);
codec_mm_dump_all_scatters();
/*codec_mm_dump_all_slots(); */
codec_mm_scatter_free(sc[0]);
codec_mm_scatter_free(sc[1]);
codec_mm_scatter_free(sc[2]);
codec_mm_dump_all_scatters();
/*codec_mm_dump_all_slots(); */
#endif
#if 0
struct codec_mm_scatter *sc[64];
INFO_LOG("codec_mm_scatter_mgt_test end dump info.11..\n");
codec_mm_scatter_info_dump(NULL, 0);
sc[0] = codec_mm_scatter_alloc(1024, 512);
INFO_LOG("codec_mm_scatter_mgt_test end dump info.22..\n");
codec_mm_scatter_info_dump(NULL, 0);
codec_mm_dump_all_scatters();
codec_mm_dump_all_slots();
sc[1] = codec_mm_scatter_alloc(128, 32);
sc[2] = codec_mm_scatter_alloc(128, 64);
sc[3] = codec_mm_scatter_alloc(128, 128);
INFO_LOG("codec_mm_scatter_mgt_test end dump info.33..\n");
codec_mm_scatter_info_dump(NULL, 0);
codec_mm_dump_all_scatters();
codec_mm_dump_all_slots();
codec_mm_scatter_free_tail_pages(sc[0], 128); /* 128 */
codec_mm_scatter_free_tail_pages(sc[1], 16); /* 16 */
codec_mm_scatter_free_tail_pages(sc[2], 4); /* 4/4 */
INFO_LOG("codec_mm_scatter_mgt_test end dump info.44..\n");
codec_mm_scatter_info_dump(NULL, 0);
codec_mm_dump_all_scatters();
codec_mm_dump_all_slots();
codec_mm_scatter_info_dump(NULL, 0);
codec_mm_scatter_free(sc[1]); /* 0 */
sc[4] = codec_mm_scatter_alloc(128, 32); /* 32 */
sc[5] = codec_mm_scatter_alloc(512, 256); /* 256 */
codec_mm_scatter_alloc_want_pages(sc[2], 44); /* 44-->//48 */
INFO_LOG("codec_mm_scatter_mgt_test end dump info.55.\n");
codec_mm_scatter_info_dump(NULL, 0);
codec_mm_dump_all_scatters();
codec_mm_dump_all_slots();
codec_mm_scatter_free(sc[0]);
codec_mm_scatter_free(sc[2]);
codec_mm_scatter_free(sc[3]);
INFO_LOG("codec_mm_scatter_mgt_test end dump info.66..\n");
codec_mm_scatter_info_dump(NULL, 0);
codec_mm_dump_all_scatters();
codec_mm_scatter_free(sc[4]);
codec_mm_scatter_free(sc[5]);
INFO_LOG("codec_mm_scatter_mgt_test end dump info.77..\n");
codec_mm_scatter_info_dump(NULL, 0);
codec_mm_dump_all_scatters();
codec_mm_dump_all_slots();
#endif
return 0;
}
EXPORT_SYMBOL(codec_mm_scatter_mgt_test);
/*
*mode:0,dump, 1,alloc 2,more,3,free some,4,free all
*0:dump ALL
*1: alloc id,num
*2: alloc more id,num
*3: free tail id,start
*4: free all id
*5:dump id
*6:dump all free slots
*/
int codec_mm_scatter_test(int mode, int p1, int p2)
{
static int init;
static struct codec_mm_scatter *sc[64];
if (!init) {
init++;
memset(sc, 0, sizeof(sc));
}
switch (mode) {
case 1: /*alloc */
INFO_LOG(" alloc sc[%d] num %d:\n", p1, p2);
if (p1 > 0 && p1 < 64) {
if (sc[p1])
codec_mm_scatter_free_on_nouser(
(struct codec_mm_scatter_mgt *)sc[p1]->manager,
sc[p1]);
sc[p1] = codec_mm_scatter_alloc(p2 * 2, p2, 0);
}
break;
case 2: /*alloc more */
INFO_LOG(" alloc more sc[%d] num %d\n", p1, p2);
if (p1 > 0 && p1 < 64 && sc[p1])
codec_mm_scatter_alloc_want_pages(sc[p1], p2);
break;
case 3: /*alloc tails */
INFO_LOG(" free some sc[%d] start free id %d\n", p1, p2);
if (p1 > 0 && p1 < 64 && sc[p1])
codec_mm_scatter_free_tail_pages(sc[p1], p2);
break;
case 4:
INFO_LOG(" free sc[%d] all\n", p1);
if (p1 > 0 && p1 < 64 && sc[p1]) {
codec_mm_scatter_free_on_nouser(
(struct codec_mm_scatter_mgt *)sc[p1]->manager,
sc[p1]);
sc[p1] = NULL;
}
break;
case 5:
INFO_LOG(" sc %d info:\n", p1);
if (p1 > 0 && p1 < 64 && sc[p1])
codec_mm_dump_scatter(sc[p1], NULL, 0);
break;
case 0:
default:{
int i;
INFO_LOG(" dump all test sc info:\n");
for (i = 0; i < 64; i++) {
if (sc[i] != NULL) {
INFO_LOG(" alloc sc[%d] has data\n", i);
codec_mm_dump_scatter(sc[i], NULL, 0);
}
}
}
break;
}
return 0;
}
EXPORT_SYMBOL(codec_mm_scatter_test);