mirror of
https://github.com/nyanmisaka/mpp.git
synced 2025-10-09 02:50:06 +08:00

When there are a lot of buffer groups created the search speed will be slow. We should use hashtable to speed up. Change-Id: I9ceeaa5a72eedf81597eeb83b34d417c74ceae04 Signed-off-by: Herman Chen <herman.chen@rock-chips.com>
950 lines
27 KiB
C++
950 lines
27 KiB
C++
/*
|
|
* Copyright 2015 Rockchip Electronics Co. LTD
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#define MODULE_TAG "mpp_buffer"
|
|
|
|
#include <string.h>
|
|
|
|
#include "mpp_log.h"
|
|
#include "mpp_mem.h"
|
|
#include "mpp_env.h"
|
|
#include "mpp_hash.h"
|
|
|
|
#include "mpp_buffer_impl.h"
|
|
|
|
#define MAX_GROUP_BIT 8
|
|
#define MAX_MISC_GROUP_BIT 3
|
|
#define BUFFER_OPS_MAX_COUNT 1024
|
|
|
|
#define SEARCH_GROUP_BY_ID(id) ((MppBufferService::get_instance())->get_group_by_id(id))
|
|
|
|
typedef MPP_RET (*BufferOp)(MppAllocator allocator, MppBufferInfo *data);
|
|
|
|
typedef enum MppBufOps_e {
|
|
GRP_CREATE,
|
|
GRP_RELEASE,
|
|
GRP_RESET,
|
|
GRP_ORPHAN,
|
|
GRP_DESTROY,
|
|
|
|
GRP_OPS_BUTT = GRP_DESTROY,
|
|
BUF_COMMIT,
|
|
BUF_CREATE,
|
|
BUF_MMAP,
|
|
BUF_REF_INC,
|
|
BUF_REF_DEC,
|
|
BUF_DISCARD,
|
|
BUF_DESTROY,
|
|
BUF_OPS_BUTT,
|
|
} MppBufOps;
|
|
|
|
typedef struct MppBufLog_t {
|
|
struct list_head list;
|
|
RK_U32 group_id;
|
|
RK_S32 buffer_id;
|
|
MppBufOps ops;
|
|
RK_S32 ref_count;
|
|
const char *caller;
|
|
} MppBufLog;
|
|
|
|
// use this class only need it to init legacy group before main
|
|
class MppBufferService
|
|
{
|
|
private:
|
|
|
|
// avoid any unwanted function
|
|
MppBufferService();
|
|
~MppBufferService();
|
|
MppBufferService(const MppBufferService &);
|
|
MppBufferService &operator=(const MppBufferService &);
|
|
|
|
// buffer group final release function
|
|
void destroy_group(MppBufferGroupImpl *group);
|
|
|
|
RK_U32 get_group_id();
|
|
RK_U32 group_id;
|
|
RK_U32 group_count;
|
|
RK_U32 finalizing;
|
|
RK_U32 finished;
|
|
|
|
RK_U32 total_size;
|
|
RK_U32 total_max;
|
|
|
|
// misc group for internal / externl buffer with different type
|
|
RK_U32 misc[MPP_BUFFER_MODE_BUTT][MPP_BUFFER_TYPE_BUTT];
|
|
RK_U32 misc_count;
|
|
|
|
struct list_head mListGroup;
|
|
DECLARE_HASHTABLE(mHashGroup, MAX_GROUP_BIT);
|
|
|
|
// list for used buffer which do not have group
|
|
struct list_head mListOrphan;
|
|
|
|
public:
|
|
static MppBufferService *get_instance() {
|
|
static MppBufferService instance;
|
|
return &instance;
|
|
}
|
|
static Mutex *get_lock() {
|
|
static Mutex lock;
|
|
return &lock;
|
|
}
|
|
|
|
MppBufferGroupImpl *get_group(const char *tag, const char *caller,
|
|
MppBufferMode mode, MppBufferType type,
|
|
RK_U32 is_misc);
|
|
RK_U32 get_misc(MppBufferMode mode, MppBufferType type);
|
|
void put_group(MppBufferGroupImpl *group);
|
|
MppBufferGroupImpl *get_group_by_id(RK_U32 id);
|
|
void dump(const char *info);
|
|
RK_U32 is_finalizing();
|
|
void inc_total(RK_U32 size);
|
|
void dec_total(RK_U32 size);
|
|
RK_U32 get_total_now() { return total_size; };
|
|
RK_U32 get_total_max() { return total_max; };
|
|
};
|
|
|
|
static const char *mode2str[MPP_BUFFER_MODE_BUTT] = {
|
|
"internal",
|
|
"external",
|
|
};
|
|
|
|
static const char *type2str[MPP_BUFFER_TYPE_BUTT] = {
|
|
"normal",
|
|
"ion",
|
|
"dma-buf",
|
|
"drm",
|
|
};
|
|
static const char *ops2str[BUF_OPS_BUTT] = {
|
|
"grp create ",
|
|
"grp release",
|
|
"grp reset",
|
|
"grp orphan",
|
|
"grp destroy",
|
|
|
|
"buf commit ",
|
|
"buf create ",
|
|
"buf mmap ",
|
|
"buf ref inc",
|
|
"buf ref dec",
|
|
"buf discard",
|
|
"buf destroy",
|
|
};
|
|
|
|
RK_U32 mpp_buffer_debug = 0;
|
|
|
|
static void buffer_group_add_log(MppBufferGroupImpl *group,
|
|
MppBufferImpl *buffer, MppBufOps ops, const char* caller)
|
|
{
|
|
if (group->log_runtime_en) {
|
|
if (buffer) {
|
|
mpp_log("group %2d buffer %2d fd %2d ops %s ref_count %d caller %s\n",
|
|
group->group_id, buffer->buffer_id, buffer->info.fd,
|
|
ops2str[ops], buffer->ref_count, caller);
|
|
} else {
|
|
mpp_log("group %2d mode %d type %d ops %s\n", group->group_id,
|
|
group->mode, group->type, ops2str[ops]);
|
|
}
|
|
}
|
|
if (group->log_history_en) {
|
|
struct list_head *logs = &group->list_logs;
|
|
MppBufLog *log = mpp_malloc(MppBufLog, 1);
|
|
if (log) {
|
|
INIT_LIST_HEAD(&log->list);
|
|
log->group_id = group->group_id;
|
|
log->buffer_id = (buffer) ? (buffer->buffer_id) : (-1);
|
|
log->ops = ops;
|
|
log->ref_count = (buffer) ? (buffer->ref_count) : (0);
|
|
log->caller = caller;
|
|
|
|
if (group->log_count >= BUFFER_OPS_MAX_COUNT) {
|
|
struct list_head *tmp = logs->next;
|
|
list_del_init(tmp);
|
|
mpp_free(list_entry(tmp, MppBufLog, list));
|
|
group->log_count--;
|
|
}
|
|
list_add_tail(&log->list, logs);
|
|
group->log_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void buffer_group_dump_log(MppBufferGroupImpl *group)
|
|
{
|
|
if (group->log_history_en) {
|
|
struct list_head *logs = &group->list_logs;
|
|
|
|
while (!list_empty(logs)) {
|
|
struct list_head *tmp = logs->next;
|
|
MppBufLog *log = list_entry(tmp, MppBufLog, list);
|
|
list_del_init(tmp);
|
|
if (log->buffer_id >= 0) {
|
|
mpp_log("group %2d buffer %2d ops %s ref_count %d caller %s\n",
|
|
group->group_id, log->buffer_id,
|
|
ops2str[log->ops], log->ref_count, log->caller);
|
|
} else {
|
|
mpp_log("group %3d ops %s\n", group->group_id,
|
|
ops2str[log->ops]);
|
|
}
|
|
mpp_free(log);
|
|
group->log_count--;
|
|
}
|
|
}
|
|
}
|
|
|
|
static MPP_RET deinit_buffer_no_lock(MppBufferImpl *buffer, const char *caller)
|
|
{
|
|
if (!MppBufferService::get_instance()->is_finalizing()) {
|
|
mpp_assert(buffer->ref_count == 0);
|
|
mpp_assert(buffer->used == 0);
|
|
}
|
|
|
|
list_del_init(&buffer->list_status);
|
|
MppBufferGroupImpl *group = SEARCH_GROUP_BY_ID(buffer->group_id);
|
|
if (group) {
|
|
BufferOp func = (group->mode == MPP_BUFFER_INTERNAL) ?
|
|
(group->alloc_api->free) :
|
|
(group->alloc_api->release);
|
|
func(group->allocator, &buffer->info);
|
|
group->usage -= buffer->info.size;
|
|
group->buffer_count--;
|
|
|
|
if (group->mode == MPP_BUFFER_INTERNAL)
|
|
MppBufferService::get_instance()->dec_total(buffer->info.size);
|
|
|
|
buffer_group_add_log(group, buffer, BUF_DESTROY, caller);
|
|
|
|
if (group->is_orphan && !group->usage && !group->is_finalizing) {
|
|
MppBufferService::get_instance()->put_group(group);
|
|
}
|
|
} else {
|
|
mpp_assert(MppBufferService::get_instance()->is_finalizing());
|
|
}
|
|
|
|
mpp_free(buffer);
|
|
|
|
return MPP_OK;
|
|
}
|
|
|
|
static MPP_RET inc_buffer_ref_no_lock(MppBufferImpl *buffer, const char *caller)
|
|
{
|
|
MPP_RET ret = MPP_OK;
|
|
MppBufferGroupImpl *group = SEARCH_GROUP_BY_ID(buffer->group_id);
|
|
if (!buffer->used) {
|
|
// NOTE: when increasing ref_count the unused buffer must be under certain group
|
|
mpp_assert(group);
|
|
buffer->used = 1;
|
|
if (group) {
|
|
list_del_init(&buffer->list_status);
|
|
list_add_tail(&buffer->list_status, &group->list_used);
|
|
group->count_used++;
|
|
group->count_unused--;
|
|
} else {
|
|
mpp_err_f("unused buffer without group\n");
|
|
ret = MPP_NOK;
|
|
}
|
|
}
|
|
buffer_group_add_log(group, buffer, BUF_REF_INC, caller);
|
|
buffer->ref_count++;
|
|
return ret;
|
|
}
|
|
|
|
static void dump_buffer_info(MppBufferImpl *buffer)
|
|
{
|
|
mpp_log("buffer %p fd %4d size %10d ref_count %3d discard %d caller %s\n",
|
|
buffer, buffer->info.fd, buffer->info.size,
|
|
buffer->ref_count, buffer->discard, buffer->caller);
|
|
}
|
|
|
|
MPP_RET mpp_buffer_create(const char *tag, const char *caller,
|
|
MppBufferGroupImpl *group, MppBufferInfo *info,
|
|
MppBufferImpl **buffer)
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
MPP_BUF_FUNCTION_ENTER();
|
|
|
|
MPP_RET ret = MPP_OK;
|
|
BufferOp func = NULL;
|
|
MppBufferImpl *p = NULL;
|
|
|
|
if (NULL == group) {
|
|
mpp_err_f("can not create buffer without group\n");
|
|
ret = MPP_NOK;
|
|
goto RET;
|
|
}
|
|
|
|
if (group->limit_count && group->buffer_count >= group->limit_count) {
|
|
if (group->log_runtime_en)
|
|
mpp_log_f("group %d reach count limit %d\n", group->group_id, group->limit_count);
|
|
ret = MPP_NOK;
|
|
goto RET;
|
|
}
|
|
|
|
if (group->limit_size && info->size > group->limit_size) {
|
|
mpp_err_f("required size %d reach group size limit %d\n", info->size, group->limit_size);
|
|
ret = MPP_NOK;
|
|
goto RET;
|
|
}
|
|
|
|
p = mpp_calloc(MppBufferImpl, 1);
|
|
if (NULL == p) {
|
|
mpp_err_f("failed to allocate context\n");
|
|
ret = MPP_ERR_MALLOC;
|
|
goto RET;
|
|
}
|
|
|
|
func = (group->mode == MPP_BUFFER_INTERNAL) ?
|
|
(group->alloc_api->alloc) : (group->alloc_api->import);
|
|
ret = func(group->allocator, info);
|
|
if (MPP_OK != ret) {
|
|
mpp_err_f("failed to create buffer with size %d\n", info->size);
|
|
mpp_free(p);
|
|
ret = MPP_ERR_MALLOC;
|
|
goto RET;
|
|
}
|
|
if (group->mode == MPP_BUFFER_INTERNAL)
|
|
MppBufferService::get_instance()->inc_total(info->size);
|
|
|
|
p->info = *info;
|
|
p->mode = group->mode;
|
|
|
|
if (NULL == tag)
|
|
tag = group->tag;
|
|
|
|
strncpy(p->tag, tag, sizeof(p->tag));
|
|
p->caller = caller;
|
|
p->group_id = group->group_id;
|
|
p->buffer_id = group->buffer_id;
|
|
INIT_LIST_HEAD(&p->list_status);
|
|
list_add_tail(&p->list_status, &group->list_unused);
|
|
|
|
group->buffer_id++;
|
|
group->usage += info->size;
|
|
group->buffer_count++;
|
|
group->count_unused++;
|
|
|
|
buffer_group_add_log(group, p,
|
|
(group->mode == MPP_BUFFER_INTERNAL) ? (BUF_CREATE) : (BUF_COMMIT),
|
|
caller);
|
|
|
|
if (buffer) {
|
|
inc_buffer_ref_no_lock(p, caller);
|
|
*buffer = p;
|
|
}
|
|
|
|
if (group->callback)
|
|
group->callback(group->arg, group);
|
|
RET:
|
|
MPP_BUF_FUNCTION_LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
MPP_RET mpp_buffer_mmap(MppBufferImpl *buffer, const char* caller)
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
MPP_BUF_FUNCTION_ENTER();
|
|
|
|
MPP_RET ret = MPP_NOK;
|
|
MppBufferGroupImpl *group = SEARCH_GROUP_BY_ID(buffer->group_id);
|
|
|
|
if (group && group->alloc_api && group->alloc_api->mmap) {
|
|
ret = group->alloc_api->mmap(group->allocator, &buffer->info);
|
|
|
|
buffer_group_add_log(group, buffer, BUF_MMAP, caller);
|
|
}
|
|
|
|
if (ret)
|
|
mpp_err_f("buffer %p group %p fd %d map failed caller %s\n",
|
|
buffer, group, buffer->info.fd, caller);
|
|
|
|
MPP_BUF_FUNCTION_LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
MPP_RET mpp_buffer_ref_inc(MppBufferImpl *buffer, const char* caller)
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
MPP_BUF_FUNCTION_ENTER();
|
|
|
|
MPP_RET ret = inc_buffer_ref_no_lock(buffer, caller);
|
|
|
|
MPP_BUF_FUNCTION_LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
|
|
MPP_RET mpp_buffer_ref_dec(MppBufferImpl *buffer, const char* caller)
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
MPP_BUF_FUNCTION_ENTER();
|
|
|
|
MPP_RET ret = MPP_OK;
|
|
MppBufferGroupImpl *group = SEARCH_GROUP_BY_ID(buffer->group_id);
|
|
if (group)
|
|
buffer_group_add_log(group, buffer, BUF_REF_DEC, caller);
|
|
|
|
if (buffer->ref_count <= 0) {
|
|
mpp_err_f("found non-positive ref_count %d caller %s\n",
|
|
buffer->ref_count, buffer->caller);
|
|
mpp_abort();
|
|
ret = MPP_NOK;
|
|
} else {
|
|
buffer->ref_count--;
|
|
if (0 == buffer->ref_count) {
|
|
buffer->used = 0;
|
|
list_del_init(&buffer->list_status);
|
|
if (group->is_misc) {
|
|
deinit_buffer_no_lock(buffer, caller);
|
|
} else {
|
|
if (buffer->discard) {
|
|
deinit_buffer_no_lock(buffer, caller);
|
|
} else {
|
|
list_add_tail(&buffer->list_status, &group->list_unused);
|
|
group->count_unused++;
|
|
}
|
|
}
|
|
group->count_used--;
|
|
if (group->callback)
|
|
group->callback(group->arg, group);
|
|
}
|
|
}
|
|
|
|
MPP_BUF_FUNCTION_LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
MppBufferImpl *mpp_buffer_get_unused(MppBufferGroupImpl *p, size_t size)
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
MPP_BUF_FUNCTION_ENTER();
|
|
|
|
MppBufferImpl *buffer = NULL;
|
|
|
|
if (!list_empty(&p->list_unused)) {
|
|
MppBufferImpl *pos, *n;
|
|
RK_S32 found = 0;
|
|
RK_S32 search_count = 0;
|
|
|
|
list_for_each_entry_safe(pos, n, &p->list_unused, MppBufferImpl, list_status) {
|
|
mpp_buf_dbg(MPP_BUF_DBG_CHECK_SIZE, "request size %d on buf idx %d size %d\n",
|
|
size, pos->buffer_id, pos->info.size);
|
|
if (pos->info.size >= size) {
|
|
buffer = pos;
|
|
inc_buffer_ref_no_lock(buffer, __FUNCTION__);
|
|
found = 1;
|
|
break;
|
|
} else {
|
|
if (MPP_BUFFER_INTERNAL == p->mode) {
|
|
deinit_buffer_no_lock(pos, __FUNCTION__);
|
|
p->count_unused--;
|
|
} else
|
|
search_count++;
|
|
}
|
|
}
|
|
|
|
if (!found && search_count)
|
|
mpp_err_f("can not found match buffer with size larger than %d\n", size);
|
|
}
|
|
|
|
MPP_BUF_FUNCTION_LEAVE();
|
|
return buffer;
|
|
}
|
|
|
|
RK_U32 mpp_buffer_to_addr(MppBuffer buffer, size_t offset)
|
|
{
|
|
MppBufferImpl *impl = (MppBufferImpl *)buffer;
|
|
|
|
if (NULL == impl) {
|
|
mpp_err_f("NULL buffer convert to zero address\n");
|
|
return 0;
|
|
}
|
|
|
|
if (impl->info.fd >= (1 << 10)) {
|
|
mpp_err_f("buffer fd %d is too large\n");
|
|
return 0;
|
|
}
|
|
|
|
if (impl->offset + offset >= SZ_4M) {
|
|
mpp_err_f("offset %d + %d is larger than 4M use extra info to send offset\n");
|
|
return 0;
|
|
}
|
|
|
|
RK_U32 addr = impl->info.fd + ((impl->offset + offset) << 10);
|
|
|
|
return addr;
|
|
}
|
|
|
|
MPP_RET mpp_buffer_group_init(MppBufferGroupImpl **group, const char *tag, const char *caller,
|
|
MppBufferMode mode, MppBufferType type)
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
|
|
mpp_assert(caller);
|
|
MPP_BUF_FUNCTION_ENTER();
|
|
|
|
*group = MppBufferService::get_instance()->get_group(tag, caller, mode, type, 0);
|
|
|
|
MPP_BUF_FUNCTION_LEAVE();
|
|
return ((*group) ? (MPP_OK) : (MPP_NOK));
|
|
}
|
|
|
|
MPP_RET mpp_buffer_group_deinit(MppBufferGroupImpl *p)
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
if (NULL == p) {
|
|
mpp_err_f("found NULL pointer\n");
|
|
return MPP_ERR_NULL_PTR;
|
|
}
|
|
|
|
MPP_BUF_FUNCTION_ENTER();
|
|
|
|
MppBufferService::get_instance()->put_group(p);
|
|
|
|
MPP_BUF_FUNCTION_LEAVE();
|
|
return MPP_OK;
|
|
}
|
|
|
|
MPP_RET mpp_buffer_group_reset(MppBufferGroupImpl *p)
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
if (NULL == p) {
|
|
mpp_err_f("found NULL pointer\n");
|
|
return MPP_ERR_NULL_PTR;
|
|
}
|
|
|
|
MPP_BUF_FUNCTION_ENTER();
|
|
|
|
buffer_group_add_log(p, NULL, GRP_RESET, NULL);
|
|
|
|
if (!list_empty(&p->list_used)) {
|
|
MppBufferImpl *pos, *n;
|
|
list_for_each_entry_safe(pos, n, &p->list_used, MppBufferImpl, list_status) {
|
|
buffer_group_add_log(p, pos, BUF_DISCARD, NULL);
|
|
pos->discard = 1;
|
|
}
|
|
}
|
|
|
|
// remove unused list
|
|
if (!list_empty(&p->list_unused)) {
|
|
MppBufferImpl *pos, *n;
|
|
list_for_each_entry_safe(pos, n, &p->list_unused, MppBufferImpl, list_status) {
|
|
deinit_buffer_no_lock(pos, __FUNCTION__);
|
|
p->count_unused--;
|
|
}
|
|
}
|
|
|
|
MPP_BUF_FUNCTION_LEAVE();
|
|
return MPP_OK;
|
|
}
|
|
|
|
MPP_RET mpp_buffer_group_set_callback(MppBufferGroupImpl *p,
|
|
MppBufCallback callback, void *arg)
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
if (NULL == p) {
|
|
mpp_err_f("found NULL pointer\n");
|
|
return MPP_ERR_NULL_PTR;
|
|
}
|
|
|
|
MPP_BUF_FUNCTION_ENTER();
|
|
|
|
p->callback = callback;
|
|
p->arg = arg;
|
|
|
|
MPP_BUF_FUNCTION_LEAVE();
|
|
return MPP_OK;
|
|
}
|
|
|
|
void mpp_buffer_group_dump(MppBufferGroupImpl *group, const char *caller)
|
|
{
|
|
mpp_log("\ndumping buffer group %p id %d from %s\n", group,
|
|
group->group_id, caller);
|
|
mpp_log("mode %s\n", mode2str[group->mode]);
|
|
mpp_log("type %s\n", type2str[group->type]);
|
|
mpp_log("limit size %d count %d\n", group->limit_size, group->limit_count);
|
|
|
|
mpp_log("used buffer count %d\n", group->count_used);
|
|
|
|
MppBufferImpl *pos, *n;
|
|
list_for_each_entry_safe(pos, n, &group->list_used, MppBufferImpl, list_status) {
|
|
dump_buffer_info(pos);
|
|
}
|
|
|
|
mpp_log("unused buffer count %d\n", group->count_unused);
|
|
list_for_each_entry_safe(pos, n, &group->list_unused, MppBufferImpl, list_status) {
|
|
dump_buffer_info(pos);
|
|
}
|
|
|
|
buffer_group_dump_log(group);
|
|
}
|
|
|
|
void mpp_buffer_service_dump(const char *info)
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
|
|
MppBufferService::get_instance()->dump(info);
|
|
}
|
|
|
|
void MppBufferService::inc_total(RK_U32 size)
|
|
{
|
|
total_size += size;
|
|
if (total_size > total_max)
|
|
total_max = total_size;
|
|
}
|
|
|
|
void MppBufferService::dec_total(RK_U32 size)
|
|
{
|
|
total_size -= size;
|
|
}
|
|
|
|
RK_U32 mpp_buffer_total_now()
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
return MppBufferService::get_instance()->get_total_now();
|
|
}
|
|
|
|
RK_U32 mpp_buffer_total_max()
|
|
{
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
return MppBufferService::get_instance()->get_total_max();
|
|
}
|
|
|
|
MppBufferGroupImpl *mpp_buffer_get_misc_group(MppBufferMode mode, MppBufferType type)
|
|
{
|
|
MppBufferGroupImpl *misc;
|
|
RK_U32 id;
|
|
|
|
type = (MppBufferType)(type & MPP_BUFFER_TYPE_MASK);
|
|
if (type == MPP_BUFFER_TYPE_NORMAL)
|
|
return NULL;
|
|
|
|
mpp_assert(mode < MPP_BUFFER_MODE_BUTT);
|
|
mpp_assert(type < MPP_BUFFER_TYPE_BUTT);
|
|
|
|
AutoMutex auto_lock(MppBufferService::get_lock());
|
|
|
|
id = MppBufferService::get_instance()->get_misc(mode, type);
|
|
if (!id) {
|
|
char tag[32];
|
|
RK_S32 offset = 0;
|
|
|
|
offset += snprintf(tag + offset, sizeof(tag) - offset, "misc");
|
|
offset += snprintf(tag + offset, sizeof(tag) - offset, "_%s",
|
|
type == MPP_BUFFER_TYPE_ION ? "ion" :
|
|
type == MPP_BUFFER_TYPE_DRM ? "drm" : "na");
|
|
offset += snprintf(tag + offset, sizeof(tag) - offset, "_%s",
|
|
mode == MPP_BUFFER_INTERNAL ? "int" : "ext");
|
|
|
|
misc = MppBufferService::get_instance()->get_group(tag, __FUNCTION__, mode, type, 1);
|
|
} else
|
|
misc = MppBufferService::get_instance()->get_group_by_id(id);
|
|
|
|
return misc;
|
|
}
|
|
|
|
MppBufferService::MppBufferService()
|
|
: group_id(1),
|
|
group_count(0),
|
|
finalizing(0),
|
|
finished(0),
|
|
total_size(0),
|
|
total_max(0),
|
|
misc_count(0)
|
|
{
|
|
RK_S32 i, j;
|
|
|
|
INIT_LIST_HEAD(&mListGroup);
|
|
INIT_LIST_HEAD(&mListOrphan);
|
|
|
|
// NOTE: Do not create misc group at beginning. Only create on when needed.
|
|
for (i = 0; i < MPP_BUFFER_MODE_BUTT; i++)
|
|
for (j = 0; j < MPP_BUFFER_TYPE_BUTT; j++)
|
|
misc[i][j] = 0;
|
|
|
|
for (i = 0; i < (RK_S32)HASH_SIZE(mHashGroup); i++)
|
|
INIT_HLIST_HEAD(&mHashGroup[i]);
|
|
}
|
|
|
|
MppBufferService::~MppBufferService()
|
|
{
|
|
RK_S32 i, j;
|
|
|
|
finalizing = 1;
|
|
|
|
// first remove legacy group which is the normal case
|
|
if (misc_count) {
|
|
mpp_log_f("cleaning misc group\n");
|
|
for (i = 0; i < MPP_BUFFER_MODE_BUTT; i++)
|
|
for (j = 0; j < MPP_BUFFER_TYPE_BUTT; j++) {
|
|
RK_U32 id = misc[i][j];
|
|
|
|
if (id) {
|
|
put_group(get_group_by_id(id));
|
|
misc[i][j] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// then remove the remaining group which is the leak one
|
|
if (!list_empty(&mListGroup)) {
|
|
MppBufferGroupImpl *pos, *n;
|
|
|
|
mpp_log_f("cleaning leaked group\n");
|
|
list_for_each_entry_safe(pos, n, &mListGroup, MppBufferGroupImpl, list_group) {
|
|
put_group(pos);
|
|
}
|
|
}
|
|
|
|
// remove all orphan buffer group
|
|
if (!list_empty(&mListOrphan)) {
|
|
MppBufferGroupImpl *pos, *n;
|
|
|
|
mpp_log_f("cleaning leaked buffer\n");
|
|
|
|
list_for_each_entry_safe(pos, n, &mListOrphan, MppBufferGroupImpl, list_group) {
|
|
pos->clear_on_exit = 1;
|
|
pos->is_finalizing = 1;
|
|
put_group(pos);
|
|
}
|
|
}
|
|
finished = 1;
|
|
}
|
|
|
|
RK_U32 MppBufferService::get_group_id()
|
|
{
|
|
RK_U32 id;
|
|
static RK_U32 overflowed = 0;
|
|
|
|
/* avoid 0 group id */
|
|
if (!group_id)
|
|
group_id++;
|
|
|
|
id = group_id++;
|
|
/* check overflow */
|
|
if (!id) {
|
|
overflowed = 1;
|
|
id = group_id++;
|
|
}
|
|
|
|
// avoid group_id reuse
|
|
if (overflowed) {
|
|
/* when it is overflow avoid the used id */
|
|
while (get_group_by_id(id))
|
|
id = group_id++;
|
|
}
|
|
|
|
group_count++;
|
|
|
|
return id;
|
|
}
|
|
|
|
MppBufferGroupImpl *MppBufferService::get_group(const char *tag, const char *caller,
|
|
MppBufferMode mode, MppBufferType type,
|
|
RK_U32 is_misc)
|
|
{
|
|
MppBufferType buffer_type = (MppBufferType)(type & MPP_BUFFER_TYPE_MASK);
|
|
MppBufferGroupImpl *p = mpp_calloc(MppBufferGroupImpl, 1);
|
|
if (NULL == p) {
|
|
mpp_err("MppBufferService failed to allocate group context\n");
|
|
return NULL;
|
|
}
|
|
|
|
RK_U32 id = get_group_id();
|
|
|
|
INIT_LIST_HEAD(&p->list_logs);
|
|
INIT_LIST_HEAD(&p->list_group);
|
|
INIT_LIST_HEAD(&p->list_used);
|
|
INIT_LIST_HEAD(&p->list_unused);
|
|
INIT_HLIST_NODE(&p->hlist);
|
|
|
|
mpp_env_get_u32("mpp_buffer_debug", &mpp_buffer_debug, 0);
|
|
p->log_runtime_en = (mpp_buffer_debug & MPP_BUF_DBG_OPS_RUNTIME) ? (1) : (0);
|
|
p->log_history_en = (mpp_buffer_debug & MPP_BUF_DBG_OPS_HISTORY) ? (1) : (0);
|
|
|
|
if (tag) {
|
|
snprintf(p->tag, sizeof(p->tag), "%s_%d", tag, id);
|
|
} else {
|
|
snprintf(p->tag, sizeof(p->tag), "unknown");
|
|
}
|
|
p->caller = caller;
|
|
p->mode = mode;
|
|
p->type = buffer_type;
|
|
p->limit = BUFFER_GROUP_SIZE_DEFAULT;
|
|
p->group_id = id;
|
|
p->clear_on_exit = (mpp_buffer_debug & MPP_BUF_DBG_CLR_ON_EXIT) ? (1) : (0);
|
|
p->dump_on_exit = (mpp_buffer_debug & MPP_BUF_DBG_DUMP_ON_EXIT) ? (1) : (0);
|
|
|
|
list_add_tail(&p->list_group, &mListGroup);
|
|
hash_add(mHashGroup, &p->hlist, hash_32(p->group_id, MAX_GROUP_BIT));
|
|
|
|
mpp_allocator_get(&p->allocator, &p->alloc_api, type);
|
|
|
|
buffer_group_add_log(p, NULL, GRP_CREATE, __FUNCTION__);
|
|
|
|
mpp_assert(mode < MPP_BUFFER_MODE_BUTT);
|
|
mpp_assert(buffer_type < MPP_BUFFER_TYPE_BUTT);
|
|
|
|
if (is_misc) {
|
|
misc[mode][buffer_type] = id;
|
|
p->is_misc = 1;
|
|
misc_count++;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
RK_U32 MppBufferService::get_misc(MppBufferMode mode, MppBufferType type)
|
|
{
|
|
type = (MppBufferType)(type & MPP_BUFFER_TYPE_MASK);
|
|
if (type == MPP_BUFFER_TYPE_NORMAL)
|
|
return 0;
|
|
|
|
mpp_assert(mode < MPP_BUFFER_MODE_BUTT);
|
|
mpp_assert(type < MPP_BUFFER_TYPE_BUTT);
|
|
|
|
return misc[mode][type];
|
|
}
|
|
|
|
void MppBufferService::put_group(MppBufferGroupImpl *p)
|
|
{
|
|
if (finished)
|
|
return ;
|
|
|
|
buffer_group_add_log(p, NULL, GRP_RELEASE, __FUNCTION__);
|
|
|
|
// remove unused list
|
|
if (!list_empty(&p->list_unused)) {
|
|
MppBufferImpl *pos, *n;
|
|
list_for_each_entry_safe(pos, n, &p->list_unused, MppBufferImpl, list_status) {
|
|
deinit_buffer_no_lock(pos, __FUNCTION__);
|
|
p->count_unused--;
|
|
}
|
|
}
|
|
|
|
if (list_empty(&p->list_used)) {
|
|
destroy_group(p);
|
|
} else {
|
|
if (!finalizing || (finalizing && p->dump_on_exit)) {
|
|
mpp_err("mpp_group %p tag %s caller %s mode %s type %s deinit with %d bytes not released\n",
|
|
p, p->tag, p->caller, mode2str[p->mode], type2str[p->type], p->usage);
|
|
|
|
mpp_buffer_group_dump(p, __FUNCTION__);
|
|
}
|
|
|
|
/* if clear on exit we need to release remaining buffer */
|
|
if (p->clear_on_exit) {
|
|
MppBufferImpl *pos, *n;
|
|
|
|
if (p->dump_on_exit)
|
|
mpp_err("force release all remaining buffer\n");
|
|
|
|
list_for_each_entry_safe(pos, n, &p->list_used, MppBufferImpl, list_status) {
|
|
if (p->dump_on_exit)
|
|
mpp_err("clearing buffer %p\n", pos);
|
|
pos->ref_count = 0;
|
|
pos->used = 0;
|
|
pos->discard = 0;
|
|
deinit_buffer_no_lock(pos, __FUNCTION__);
|
|
p->count_used--;
|
|
}
|
|
|
|
destroy_group(p);
|
|
} else {
|
|
// otherwise move the group to list_orphan and wait for buffer release
|
|
buffer_group_add_log(p, NULL, GRP_ORPHAN, __FUNCTION__);
|
|
list_del_init(&p->list_group);
|
|
list_add_tail(&p->list_group, &mListOrphan);
|
|
p->is_orphan = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MppBufferService::destroy_group(MppBufferGroupImpl *group)
|
|
{
|
|
MppBufferMode mode = group->mode;
|
|
MppBufferType type = group->type;
|
|
RK_U32 id = group->group_id;
|
|
|
|
mpp_assert(group->count_used == 0);
|
|
mpp_assert(group->count_unused == 0);
|
|
if (group->count_unused || group->count_used) {
|
|
mpp_err("mpp_buffer_group_deinit mismatch counter used %4d unused %4d found\n",
|
|
group->count_used, group->count_unused);
|
|
group->count_unused = 0;
|
|
group->count_used = 0;
|
|
}
|
|
|
|
buffer_group_add_log(group, NULL, GRP_DESTROY, __FUNCTION__);
|
|
|
|
if (group->log_history_en) {
|
|
struct list_head *logs = &group->list_logs;
|
|
while (!list_empty(logs)) {
|
|
struct list_head *tmp = logs->next;
|
|
list_del_init(tmp);
|
|
mpp_free(list_entry(tmp, MppBufLog, list));
|
|
group->log_count--;
|
|
}
|
|
mpp_assert(group->log_count == 0);
|
|
}
|
|
|
|
mpp_assert(group->allocator);
|
|
mpp_allocator_put(&group->allocator);
|
|
list_del_init(&group->list_group);
|
|
hash_del(&group->hlist);
|
|
mpp_free(group);
|
|
group_count--;
|
|
|
|
if (id == misc[mode][type]) {
|
|
misc[mode][type] = 0;
|
|
misc_count--;
|
|
}
|
|
}
|
|
|
|
MppBufferGroupImpl *MppBufferService::get_group_by_id(RK_U32 id)
|
|
{
|
|
MppBufferGroupImpl *impl = NULL;
|
|
RK_U32 key = hash_32(id, MAX_GROUP_BIT);
|
|
|
|
hash_for_each_possible(mHashGroup, impl, hlist, key) {
|
|
if (impl->group_id == id)
|
|
break;
|
|
}
|
|
|
|
return impl;
|
|
}
|
|
|
|
void MppBufferService::dump(const char *info)
|
|
{
|
|
MppBufferGroupImpl *group;
|
|
struct hlist_node *n;
|
|
RK_U32 key;
|
|
|
|
mpp_log("dumping all buffer groups for %s\n", info);
|
|
|
|
if (hash_empty(mHashGroup)) {
|
|
mpp_log("no buffer group can be dumped\n");
|
|
} else {
|
|
hash_for_each_safe(mHashGroup, key, n, group, hlist) {
|
|
mpp_buffer_group_dump(group, __FUNCTION__);
|
|
}
|
|
}
|
|
}
|
|
|
|
RK_U32 MppBufferService::is_finalizing()
|
|
{
|
|
return finalizing;
|
|
}
|