/src/opensips/mem/f_parallel_malloc.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2001-2003 FhG Fokus |
3 | | * Copyright (C) 2025 OpenSIPS Project |
4 | | * |
5 | | * This file is part of opensips, a free SIP server. |
6 | | * |
7 | | * opensips is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 2 of the License, or |
10 | | * (at your option) any later version |
11 | | * |
12 | | * opensips is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | | */ |
21 | | |
22 | | #ifdef F_PARALLEL_MALLOC |
23 | | |
24 | | #include <string.h> |
25 | | #include <stdio.h> |
26 | | #include <stdlib.h> |
27 | | |
28 | | #include "f_parallel_malloc.h" |
29 | | #include "../dprint.h" |
30 | | #include "../globals.h" |
31 | | #include "../statistics.h" |
32 | | |
33 | | #ifdef DBG_MALLOC |
34 | | #include "mem_dbg_hash.h" |
35 | | #endif |
36 | | |
37 | | #include "../lib/dbg/struct_hist.h" |
38 | | |
39 | 0 | #define MIN_FRAG_SIZE ROUNDTO |
40 | 0 | #define F_PARALLEL_FRAG_OVERHEAD (sizeof(struct parallel_frag)) |
41 | 0 | #define frag_is_free(_f) ((_f)->prev) |
42 | | #define frag_seems_valid(_f, _b) (!(_f)->prev || \ |
43 | | ((_b)->first_frag <= *(_f)->prev && *(_f)->prev <= (_b)->last_frag)) |
44 | | |
45 | | #define F_PARALLEL_FRAG_NEXT(f) \ |
46 | 0 | ((struct parallel_frag *)((char *)(f) + sizeof(struct parallel_frag) + (f)->size)) |
47 | | |
48 | | #define max(a,b) ( (a)>(b)?(a):(b)) |
49 | | |
50 | | /* ROUNDTO= 2^k so the following works */ |
51 | 0 | #define ROUNDTO_MASK (~((unsigned long)ROUNDTO-1)) |
52 | 0 | #define ROUNDUP(s) (((s)+(ROUNDTO-1))&ROUNDTO_MASK) |
53 | 0 | #define ROUNDDOWN(s) ((s)&ROUNDTO_MASK) |
54 | | |
55 | | /* finds the hash value for s, s=ROUNDTO multiple*/ |
56 | 0 | #define F_PARALLEL_GET_HASH(s) ( ((unsigned long)(s)<=F_PARALLEL_MALLOC_OPTIMIZE)?\ |
57 | 0 | (unsigned long)(s)/ROUNDTO: \ |
58 | 0 | F_PARALLEL_MALLOC_OPTIMIZE/ROUNDTO+big_hash_idx((s))- \ |
59 | 0 | F_PARALLEL_MALLOC_OPTIMIZE_FACTOR+1 ) |
60 | | |
61 | 0 | #define F_PARALLEL_UN_HASH(h) ( ((unsigned long)(h)<=(F_PARALLEL_MALLOC_OPTIMIZE/ROUNDTO))?\ |
62 | 0 | (unsigned long)(h)*ROUNDTO: \ |
63 | 0 | 1UL<<((unsigned long)(h)-F_PARALLEL_MALLOC_OPTIMIZE/ROUNDTO+\ |
64 | 0 | F_PARALLEL_MALLOC_OPTIMIZE_FACTOR-1)\ |
65 | 0 | ) |
66 | | |
67 | | static inline void parallel_free_minus(struct parallel_block *fm, unsigned long size) |
68 | 0 | { |
69 | |
|
70 | 0 | #if defined(DBG_MALLOC) || defined(STATISTICS) |
71 | 0 | fm->real_used+=size; |
72 | 0 | fm->used+=size; |
73 | 0 | #endif |
74 | 0 | } |
75 | | |
76 | | |
77 | | static inline void parallel_free_plus(struct parallel_block *fm, unsigned long size) |
78 | 0 | { |
79 | |
|
80 | 0 | #if defined(DBG_MALLOC) || defined(STATISTICS) |
81 | 0 | fm->real_used-=size; |
82 | 0 | fm->used-=size; |
83 | 0 | #endif |
84 | 0 | } |
85 | | |
86 | | |
87 | | /* computes hash number for big buckets*/ |
88 | | inline static unsigned long big_hash_idx(unsigned long s) |
89 | 0 | { |
90 | 0 | unsigned long idx; |
91 | | /* s is rounded => s = k*2^n (ROUNDTO=2^n) |
92 | | * index= i such that 2^i > s >= 2^(i-1) |
93 | | * |
94 | | * => index = number of the first non null bit in s*/ |
95 | 0 | idx=sizeof(long)*8-1; |
96 | 0 | for (; !(s&(1UL<<(sizeof(long)*8-1))) ; s<<=1, idx--); |
97 | 0 | return idx; |
98 | 0 | } |
99 | | |
100 | | #ifdef SHM_EXTRA_STATS |
101 | | #include "module_info.h" |
102 | | unsigned long parallel_stats_get_index(void *ptr) |
103 | | { |
104 | | if (!ptr) |
105 | | return GROUP_IDX_INVALID; |
106 | | |
107 | | return F_PARALLEL_FRAG(ptr)->statistic_index; |
108 | | } |
109 | | |
110 | | void parallel_stats_set_index(void *ptr, unsigned long idx) |
111 | | { |
112 | | if (!ptr) |
113 | | return; |
114 | | |
115 | | F_PARALLEL_FRAG(ptr)->statistic_index = idx; |
116 | | } |
117 | | #endif |
118 | | |
119 | | static inline void parallel_insert_free(struct parallel_block *fm, struct parallel_frag *frag) |
120 | 0 | { |
121 | 0 | struct parallel_frag **f; |
122 | 0 | int hash; |
123 | |
|
124 | 0 | hash=F_PARALLEL_GET_HASH(frag->size); |
125 | 0 | f=&(fm->free_hash[hash].first); |
126 | 0 | if (*f) |
127 | 0 | (*f)->block_ptr = fm; |
128 | 0 | if (frag->size > F_PARALLEL_MALLOC_OPTIMIZE){ /* because of '<=' in GET_HASH, |
129 | | (different from 0.8.1[24] on |
130 | | purpose --andrei ) */ |
131 | 0 | for(; *f; f=&((*f)->u.nxt_free)){ |
132 | 0 | (*f)->block_ptr = fm; |
133 | 0 | if (frag->size <= (*f)->size) break; |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | | /*insert it here*/ |
138 | 0 | if (*f) { |
139 | 0 | (*f)->block_ptr = fm; |
140 | 0 | } |
141 | |
|
142 | 0 | frag->prev = f; |
143 | 0 | frag->u.nxt_free=*f; |
144 | 0 | frag->block_ptr = fm; |
145 | |
|
146 | 0 | if( *f ) { |
147 | 0 | (*f)->block_ptr = fm; |
148 | 0 | (*f)->prev = &(frag->u.nxt_free); |
149 | 0 | frag->u.nxt_free->block_ptr = fm; |
150 | 0 | } |
151 | |
|
152 | 0 | *f=frag; |
153 | 0 | fm->free_hash[hash].no++; |
154 | |
|
155 | 0 | frag->block_ptr = fm; |
156 | 0 | parallel_free_plus(fm, frag->size); |
157 | 0 | } |
158 | | |
159 | | static inline void parallel_remove_free(struct parallel_block *fm, struct parallel_frag *n) |
160 | 0 | { |
161 | 0 | struct parallel_frag **pf; |
162 | 0 | int hash; |
163 | |
|
164 | 0 | pf = n->prev; |
165 | 0 | hash = F_PARALLEL_GET_HASH( n->size ); |
166 | | |
167 | | /* detach */ |
168 | 0 | if (*pf) |
169 | 0 | (*pf)->block_ptr=fm; |
170 | |
|
171 | 0 | *pf=n->u.nxt_free; |
172 | 0 | if (*pf) |
173 | 0 | (*pf)->block_ptr=fm; |
174 | |
|
175 | 0 | if( n->u.nxt_free ) |
176 | 0 | n->u.nxt_free->prev = pf; |
177 | |
|
178 | 0 | fm->free_hash[hash].no--; |
179 | |
|
180 | 0 | n->prev = NULL; |
181 | 0 | n->block_ptr = fm; |
182 | |
|
183 | 0 | parallel_free_minus(fm , n->size); |
184 | |
|
185 | 0 | }; |
186 | | |
187 | | |
188 | | |
189 | | |
190 | | |
191 | | /* init malloc and return a parallel_block*/ |
192 | | struct parallel_block *parallel_malloc_init(char *address, unsigned long size, char *name, int idx) |
193 | | |
194 | 0 | { |
195 | 0 | char *start; |
196 | 0 | char *end; |
197 | 0 | struct parallel_block *fm; |
198 | 0 | unsigned long init_overhead; |
199 | | |
200 | | /* make address and size multiple of 8*/ |
201 | 0 | start=(char*)ROUNDUP((unsigned long) address); |
202 | 0 | LM_DBG("F_OPTIMIZE=%lu, /ROUNDTO=%lu, %lu-bytes aligned\n", |
203 | 0 | F_PARALLEL_MALLOC_OPTIMIZE, F_PARALLEL_MALLOC_OPTIMIZE/ROUNDTO, |
204 | 0 | (unsigned long)ROUNDTO); |
205 | 0 | LM_DBG("F_HASH_SIZE=%lu, parallel_block size=%zu, frag_size=%zu\n", |
206 | 0 | F_PARALLEL_HASH_SIZE, sizeof(struct parallel_block), sizeof(struct parallel_frag)); |
207 | 0 | LM_DBG("params (%p, %lu), start=%p\n", address, size, start); |
208 | |
|
209 | 0 | if (size<(unsigned long)(start-address)) return 0; |
210 | 0 | size-=(start-address); |
211 | 0 | if (size <(MIN_FRAG_SIZE+F_PARALLEL_FRAG_OVERHEAD)) return 0; |
212 | 0 | size=ROUNDDOWN(size); |
213 | |
|
214 | 0 | init_overhead=(ROUNDUP(sizeof(struct parallel_block))+ 2 * F_PARALLEL_FRAG_OVERHEAD); |
215 | | |
216 | |
|
217 | 0 | if (size < init_overhead) |
218 | 0 | { |
219 | | /* not enough mem to create our control structures !!!*/ |
220 | 0 | return 0; |
221 | 0 | } |
222 | 0 | end=start+size; |
223 | 0 | fm=(struct parallel_block *)start; |
224 | 0 | memset(fm, 0, sizeof(struct parallel_block)); |
225 | 0 | fm->name = name; |
226 | 0 | fm->size=size; |
227 | |
|
228 | 0 | fm->idx = idx; |
229 | |
|
230 | 0 | #if defined(DBG_MALLOC) || defined(STATISTICS) |
231 | 0 | fm->used=size-init_overhead; |
232 | 0 | fm->real_used=size; |
233 | 0 | fm->max_real_used=init_overhead; |
234 | 0 | fm->fragments = 0; |
235 | 0 | #endif |
236 | |
|
237 | 0 | fm->first_frag=(struct parallel_frag *)(start+ROUNDUP(sizeof(struct parallel_block))); |
238 | 0 | fm->last_frag=(struct parallel_frag *)(end-sizeof(struct parallel_frag)); |
239 | |
|
240 | 0 | fm->first_frag->block_ptr = fm; |
241 | 0 | fm->last_frag->block_ptr = fm; |
242 | | |
243 | | /* init initial fragment*/ |
244 | 0 | fm->first_frag->size=size-init_overhead; |
245 | 0 | fm->last_frag->size=0; |
246 | |
|
247 | 0 | fm->last_frag->prev=NULL; |
248 | 0 | fm->first_frag->prev=NULL; |
249 | | |
250 | | /* link initial fragment into the free list*/ |
251 | |
|
252 | 0 | parallel_insert_free(fm, fm->first_frag); |
253 | |
|
254 | 0 | return fm; |
255 | 0 | } |
256 | | |
257 | | #include "f_parallel_malloc_dyn.h" |
258 | | |
259 | | #if !defined INLINE_ALLOC && defined DBG_MALLOC |
260 | | #undef DBG_MALLOC |
261 | | #include "f_parallel_malloc_dyn.h" |
262 | | #define DBG_MALLOC |
263 | | #endif |
264 | | |
265 | | #ifdef SHM_EXTRA_STATS |
266 | | void parallel_stats_core_init(struct parallel_block *fm, int core_index) |
267 | | { |
268 | | struct parallel_frag *f; |
269 | | |
270 | | for (f=fm->first_frag; (char *)f < (char *)fm->last_frag; f=F_PARALLEL_FRAG_NEXT(f)) |
271 | | if (!frag_is_free(f)) |
272 | | f->statistic_index = core_index; |
273 | | } |
274 | | |
275 | | #endif |
276 | | |
277 | | |
278 | | |
279 | | |
280 | | /* fills a malloc info structure with info about the block |
281 | | * if a parameter is not supported, it will be filled with 0 */ |
282 | | void parallel_info(struct parallel_block *fm, struct mem_info *info) |
283 | 0 | { |
284 | 0 | memset(info,0, sizeof(*info)); |
285 | | /* TODO - Not implemented, need an array here */ |
286 | 0 | return; |
287 | 0 | } |
288 | | |
289 | | #endif |