xref: /rockchip-linux_mpp/osal/mpp_mem_pool.c (revision 437bfbeb9567cca9cd9080e3f6954aa9d6a94f18)
1 /* SPDX-License-Identifier: Apache-2.0 OR MIT */
2 /*
3  * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
4  */
5 
6 #define MODULE_TAG "mpp_mem_pool"
7 
8 #include <string.h>
9 
10 #include "mpp_env.h"
11 #include "mpp_mem.h"
12 #include "mpp_list.h"
13 #include "mpp_debug.h"
14 #include "mpp_singleton.h"
15 
16 #include "mpp_mem_pool.h"
17 
18 #define MEM_POOL_DBG_FLOW               (0x00000001)
19 #define MEM_POOL_DBG_EXIT               (0x00000002)
20 
21 #define mem_pool_dbg(flag, fmt, ...)    _mpp_dbg(mpp_mem_pool_debug, flag, fmt, ## __VA_ARGS__)
22 #define mem_pool_dbg_f(flag, fmt, ...)  _mpp_dbg_f(mpp_mem_pool_debug, flag, fmt, ## __VA_ARGS__)
23 
24 #define mem_pool_dbg_flow(fmt, ...)     mem_pool_dbg(MEM_POOL_DBG_FLOW, fmt, ## __VA_ARGS__)
25 #define mem_pool_dbg_exit(fmt, ...)     mem_pool_dbg(MEM_POOL_DBG_EXIT, fmt, ## __VA_ARGS__)
26 
27 #define get_srv_mem_pool(caller) \
28     ({ \
29         MppMemPoolSrv *__tmp; \
30         if (!srv_mem_pool) { \
31             mem_pool_srv_init(); \
32         } \
33         if (srv_mem_pool) { \
34             __tmp = srv_mem_pool; \
35         } else { \
36             mpp_err("mpp mem pool srv not init at %s : %s\n", __FUNCTION__, caller); \
37             __tmp = NULL; \
38         } \
39         __tmp; \
40     })
41 
42 static rk_u32 mpp_mem_pool_debug = 0;
43 
44 /*
45  * Aligning MppMemPoolNode to 8-byte
46  * Avoiding alignment traps caused by compiler optimizations
47  */
48 typedef struct MppMemPoolNode_t {
49     void                *check;
50     struct list_head    list;
51     void                *ptr;
52     rk_u64              size;
53 } MppMemPoolNode;
54 
55 typedef struct MppMemPoolImpl_t {
56     void                *check;
57     const char          *name;
58     size_t              size;
59     pthread_mutex_t     lock;
60     struct list_head    service_link;
61 
62     struct list_head    used;
63     struct list_head    unused;
64     rk_s32              used_count;
65     rk_s32              unused_count;
66 
67     /* extra flag for C++ static destruction order error */
68     rk_s32              finalized;
69 } MppMemPoolImpl;
70 
71 typedef struct  MppMemPoolService_t {
72     struct list_head    list;
73     pthread_mutex_t     lock;
74 } MppMemPoolSrv;
75 
76 static MppMemPoolSrv *srv_mem_pool = NULL;
77 
mem_pool_srv_init()78 static void mem_pool_srv_init()
79 {
80     MppMemPoolSrv *srv = srv_mem_pool;
81 
82     mpp_env_get_u32("mpp_mem_pool_debug", &mpp_mem_pool_debug, 0);
83 
84     if (srv)
85         return;
86 
87     srv = mpp_malloc(MppMemPoolSrv, 1);
88     if (!srv) {
89         mpp_err_f("failed to allocate pool service\n");
90         return;
91     }
92 
93     srv_mem_pool = srv;
94 
95     {
96         pthread_mutexattr_t attr;
97 
98         pthread_mutexattr_init(&attr);
99         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
100         pthread_mutex_init(&srv->lock, &attr);
101         pthread_mutexattr_destroy(&attr);
102     }
103 
104     INIT_LIST_HEAD(&srv->list);
105 }
106 
put_pool(MppMemPoolSrv * srv,MppMemPoolImpl * impl,const char * caller)107 static void put_pool(MppMemPoolSrv *srv, MppMemPoolImpl *impl, const char *caller)
108 {
109     MppMemPoolNode *node, *m;
110 
111     if (impl != impl->check) {
112         mpp_err_f("invalid mem impl %p check %p at %s\n", impl, impl->check, caller);
113         return;
114     }
115 
116     if (impl->finalized)
117         return;
118 
119     pthread_mutex_lock(&impl->lock);
120 
121     if (!list_empty(&impl->unused)) {
122         list_for_each_entry_safe(node, m, &impl->unused, MppMemPoolNode, list) {
123             MPP_FREE(node);
124             impl->unused_count--;
125         }
126     }
127 
128     if (!list_empty(&impl->used)) {
129         mpp_err_f("pool %-16s found %d used buffer size %4d at %s\n",
130                   impl->name, impl->used_count, impl->size, caller);
131 
132         list_for_each_entry_safe(node, m, &impl->used, MppMemPoolNode, list) {
133             MPP_FREE(node);
134             impl->used_count--;
135         }
136     }
137 
138     if (impl->used_count || impl->unused_count)
139         mpp_err_f("pool %-16s size %4d found leaked buffer used:unused [%d:%d] at %s\n",
140                   impl->name, impl->size, impl->used_count, impl->unused_count, caller);
141 
142     pthread_mutex_unlock(&impl->lock);
143 
144     if (srv) {
145         pthread_mutex_lock(&srv->lock);
146         list_del_init(&impl->service_link);
147         pthread_mutex_unlock(&srv->lock);
148     }
149 
150     impl->finalized = 1;
151     mpp_free(impl);
152 }
153 
mem_pool_srv_deinit()154 static void mem_pool_srv_deinit()
155 {
156     MppMemPoolSrv *srv = srv_mem_pool;
157 
158     if (!srv)
159         return;
160 
161     if (!list_empty(&srv->list)) {
162         MppMemPoolImpl *pos, *n;
163 
164         list_for_each_entry_safe(pos, n, &srv->list, MppMemPoolImpl, service_link) {
165             mem_pool_dbg_exit("pool %-16s size %4d leaked\n", pos->name, pos->size);
166             put_pool(srv, pos, __FUNCTION__);
167         }
168     }
169 
170     pthread_mutex_destroy(&srv->lock);
171 
172     mpp_free(srv);
173     srv_mem_pool = NULL;
174 }
175 
mpp_mem_pool_init(const char * name,size_t size,const char * caller)176 MppMemPool mpp_mem_pool_init(const char *name, size_t size, const char *caller)
177 {
178     MppMemPoolSrv *srv = get_srv_mem_pool(caller);
179     MppMemPoolImpl *pool;
180 
181     if (!srv)
182         return NULL;
183 
184     pool = mpp_calloc(MppMemPoolImpl, 1);
185     if (!pool)
186         return NULL;
187 
188     {
189         pthread_mutexattr_t attr;
190 
191         pthread_mutexattr_init(&attr);
192         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
193         pthread_mutex_init(&pool->lock, &attr);
194         pthread_mutexattr_destroy(&attr);
195     }
196 
197     pool->check = pool;
198     pool->name = name;
199     pool->size = size;
200     pool->used_count = 0;
201     pool->unused_count = 0;
202     pool->finalized = 0;
203 
204     INIT_LIST_HEAD(&pool->used);
205     INIT_LIST_HEAD(&pool->unused);
206     INIT_LIST_HEAD(&pool->service_link);
207 
208     pthread_mutex_lock(&srv->lock);
209     list_add_tail(&pool->service_link, &srv->list);
210     pthread_mutex_unlock(&srv->lock);
211 
212     mem_pool_dbg_flow("pool %-16s size %4d init at %s\n", pool->name, size, caller);
213 
214     return pool;
215 }
216 
mpp_mem_pool_deinit(MppMemPool pool,const char * caller)217 void mpp_mem_pool_deinit(MppMemPool pool, const char *caller)
218 {
219     MppMemPoolSrv *srv = get_srv_mem_pool(caller);
220     MppMemPoolImpl *impl = (MppMemPoolImpl *)pool;
221 
222     mem_pool_dbg_flow("pool %-16s size %4d deinit at %s\n",
223                       impl->name, impl->size, caller);
224 
225     put_pool(srv, impl, caller);
226 }
227 
mpp_mem_pool_get(MppMemPool pool,const char * caller)228 void *mpp_mem_pool_get(MppMemPool pool, const char *caller)
229 {
230     MppMemPoolImpl *impl = (MppMemPoolImpl *)pool;
231     MppMemPoolNode *node = NULL;
232     void* ptr = NULL;
233 
234     pthread_mutex_lock(&impl->lock);
235 
236     mem_pool_dbg_flow("pool %-16s size %4d get used:unused [%d:%d] at %s\n",
237                       impl->name, impl->size, impl->used_count, impl->unused_count, caller);
238 
239     if (!list_empty(&impl->unused)) {
240         node = list_first_entry(&impl->unused, MppMemPoolNode, list);
241         if (node) {
242             list_del_init(&node->list);
243             list_add_tail(&node->list, &impl->used);
244             impl->unused_count--;
245             impl->used_count++;
246             ptr = node->ptr;
247             node->check = node;
248             goto DONE;
249         }
250     }
251 
252     node = mpp_malloc_size(MppMemPoolNode, sizeof(MppMemPoolNode) + impl->size);
253     if (!node) {
254         mpp_err_f("failed to create node from size %4d pool\n", impl->size);
255         goto DONE;
256     }
257 
258     node->check = node;
259     node->ptr = (void *)(node + 1);
260     node->size = impl->size;
261     INIT_LIST_HEAD(&node->list);
262     list_add_tail(&node->list, &impl->used);
263     impl->used_count++;
264     ptr = node->ptr;
265 
266 DONE:
267     pthread_mutex_unlock(&impl->lock);
268     if (node)
269         memset(node->ptr, 0 , node->size);
270     return ptr;
271 }
272 
mpp_mem_pool_put(MppMemPool pool,void * p,const char * caller)273 void mpp_mem_pool_put(MppMemPool pool, void *p, const char *caller)
274 {
275     MppMemPoolImpl *impl = (MppMemPoolImpl *)pool;
276     MppMemPoolNode *node = (MppMemPoolNode *)((rk_u8 *)p - sizeof(MppMemPoolNode));
277 
278     if (impl != impl->check) {
279         mpp_err_f("invalid mem pool %p check %p\n", impl, impl->check);
280         return ;
281     }
282 
283     if (node != node->check) {
284         mpp_err_f("invalid mem pool ptr %p node %p check %p\n",
285                   p, node, node->check);
286         return ;
287     }
288 
289     pthread_mutex_lock(&impl->lock);
290 
291     mem_pool_dbg_flow("pool %-16s size %4d put used:unused [%d:%d] at %s\n",
292                       impl->name, impl->size, impl->used_count, impl->unused_count, caller);
293 
294     list_del_init(&node->list);
295     list_add(&node->list, &impl->unused);
296     impl->used_count--;
297     impl->unused_count++;
298     node->check = NULL;
299 
300     pthread_mutex_unlock(&impl->lock);
301 }
302 
303 MPP_SINGLETON(MPP_SGLN_MEM_POOL, mpp_mem_pool, mem_pool_srv_init, mem_pool_srv_deinit)
304