1 /* sl_malloc.c - malloc routines using a per-thread slab */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/sl_malloc.c,v 1.39.2.6 2008/02/11 23:34:15 quanah Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2008 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
20 #include <ac/string.h>
24 static struct slab_object
* slap_replenish_sopool(struct slab_heap
* sh
);
26 static void print_slheap(int level
, void *ctx
);
35 struct slab_heap
*sh
= data
;
36 int pad
= 2*sizeof(int)-1, pad_shift
;
37 int order_start
= -1, i
;
38 struct slab_object
*so
;
41 ber_memfree_x(sh
->sh_base
, NULL
);
42 ber_memfree_x(sh
, NULL
);
47 } while (pad_shift
>>= 1);
49 for (i
= 0; i
<= sh
->sh_maxorder
- order_start
; i
++) {
50 so
= LDAP_LIST_FIRST(&sh
->sh_free
[i
]);
52 struct slab_object
*so_tmp
= so
;
53 so
= LDAP_LIST_NEXT(so
, so_link
);
54 LDAP_LIST_INSERT_HEAD(&sh
->sh_sopool
, so_tmp
, so_link
);
56 ch_free(sh
->sh_map
[i
]);
61 so
= LDAP_LIST_FIRST(&sh
->sh_sopool
);
63 struct slab_object
*so_tmp
= so
;
64 so
= LDAP_LIST_NEXT(so
, so_link
);
65 if (!so_tmp
->so_blockhead
) {
66 LDAP_LIST_REMOVE(so_tmp
, so_link
);
69 so
= LDAP_LIST_FIRST(&sh
->sh_sopool
);
71 struct slab_object
*so_tmp
= so
;
72 so
= LDAP_LIST_NEXT(so
, so_link
);
75 ber_memfree_x(sh
->sh_base
, NULL
);
76 ber_memfree_x(sh
, NULL
);
80 BerMemoryFunctions slap_sl_mfuncs
=
81 { slap_sl_malloc
, slap_sl_calloc
, slap_sl_realloc
, slap_sl_free
};
86 ber_set_option( NULL
, LBER_OPT_MEMORY_FNS
, &slap_sl_mfuncs
);
90 static struct slab_heap
*slheap
;
101 struct slab_heap
*sh
;
102 ber_len_t size_shift
;
103 int pad
= 2*sizeof(int)-1, pad_shift
;
104 int order
= -1, order_start
= -1, order_end
= -1;
106 struct slab_object
*so
;
112 ldap_pvt_thread_pool_getkey(
113 ctx
, (void *)slap_sl_mem_init
, &sh_tmp
, NULL
);
120 /* round up to doubleword boundary */
126 sh
= ch_malloc(sizeof(struct slab_heap
));
127 sh
->sh_base
= ch_malloc(size
);
131 ldap_pvt_thread_pool_setkey(ctx
, (void *)slap_sl_mem_init
,
132 (void *)sh
, slap_sl_mem_destroy
, NULL
, NULL
);
134 } else if ( size
> (char *)sh
->sh_end
- (char *)sh
->sh_base
) {
137 newptr
= ch_realloc( sh
->sh_base
, size
);
138 if ( newptr
== NULL
) return NULL
;
139 sh
->sh_base
= newptr
;
141 sh
->sh_last
= sh
->sh_base
;
142 sh
->sh_end
= (char *) sh
->sh_base
+ size
;
143 sh
->sh_stack
= stack
;
146 size_shift
= size
- 1;
149 } while (size_shift
>>= 1);
154 } while (pad_shift
>>= 1);
156 order
= order_end
- order_start
+ 1;
159 sh
= (struct slab_heap
*) ch_malloc(sizeof(struct slab_heap
));
160 sh
->sh_base
= ch_malloc(size
);
164 ldap_pvt_thread_pool_setkey(ctx
, (void *)slap_sl_mem_init
,
165 (void *)sh
, slap_sl_mem_destroy
, NULL
, NULL
);
168 for (i
= 0; i
<= sh
->sh_maxorder
- order_start
; i
++) {
169 so
= LDAP_LIST_FIRST(&sh
->sh_free
[i
]);
171 struct slab_object
*so_tmp
= so
;
172 so
= LDAP_LIST_NEXT(so
, so_link
);
173 LDAP_LIST_INSERT_HEAD(&sh
->sh_sopool
, so_tmp
, so_link
);
175 ch_free(sh
->sh_map
[i
]);
177 ch_free(sh
->sh_free
);
180 so
= LDAP_LIST_FIRST(&sh
->sh_sopool
);
182 struct slab_object
*so_tmp
= so
;
183 so
= LDAP_LIST_NEXT(so
, so_link
);
184 if (!so_tmp
->so_blockhead
) {
185 LDAP_LIST_REMOVE(so_tmp
, so_link
);
188 so
= LDAP_LIST_FIRST(&sh
->sh_sopool
);
190 struct slab_object
*so_tmp
= so
;
191 so
= LDAP_LIST_NEXT(so
, so_link
);
195 if (size
> (char *)sh
->sh_end
- (char *)sh
->sh_base
) {
198 newptr
= realloc( sh
->sh_base
, size
);
199 if ( newptr
== NULL
) return NULL
;
200 sh
->sh_base
= newptr
;
203 sh
->sh_end
= (char *)sh
->sh_base
+ size
;
204 sh
->sh_maxorder
= order_end
;
206 sh
->sh_free
= (struct sh_freelist
*)
207 ch_malloc(order
* sizeof(struct sh_freelist
));
208 for (i
= 0; i
< order
; i
++) {
209 LDAP_LIST_INIT(&sh
->sh_free
[i
]);
212 LDAP_LIST_INIT(&sh
->sh_sopool
);
214 if (LDAP_LIST_EMPTY(&sh
->sh_sopool
)) {
215 slap_replenish_sopool(sh
);
217 so
= LDAP_LIST_FIRST(&sh
->sh_sopool
);
218 LDAP_LIST_REMOVE(so
, so_link
);
219 so
->so_ptr
= sh
->sh_base
;
221 LDAP_LIST_INSERT_HEAD(&sh
->sh_free
[order
-1], so
, so_link
);
223 sh
->sh_map
= (unsigned char **)
224 ch_malloc(order
* sizeof(unsigned char *));
225 for (i
= 0; i
< order
; i
++) {
226 int shiftamt
= order_start
+ 1 + i
;
227 int nummaps
= size
>> shiftamt
;
230 if (!nummaps
) nummaps
= 1;
231 sh
->sh_map
[i
] = (unsigned char *) ch_malloc(nummaps
);
232 memset(sh
->sh_map
[i
], 0, nummaps
);
234 sh
->sh_stack
= stack
;
248 /* separate from context */
249 ldap_pvt_thread_pool_setkey( ctx
, (void *)slap_sl_mem_init
,
250 NULL
, 0, NULL
, NULL
);
260 struct slab_heap
*sh
= ctx
;
261 ber_len_t size_shift
;
262 int pad
= 2*sizeof(int)-1, pad_shift
;
263 int order
= -1, order_start
= -1;
264 struct slab_object
*so_new
, *so_left
, *so_right
;
265 ber_len_t
*ptr
, *newptr
;
269 #ifdef SLAP_NO_SL_MALLOC
270 return ber_memalloc_x( size
, NULL
);
273 /* ber_set_option calls us like this */
274 if (!ctx
) return ber_memalloc_x(size
, NULL
);
276 /* round up to doubleword boundary */
277 size
+= sizeof(ber_len_t
) + pad
;
281 if ((char *)sh
->sh_last
+ size
>= (char *)sh
->sh_end
) {
282 Debug(LDAP_DEBUG_TRACE
,
283 "slap_sl_malloc of %lu bytes failed, using ch_malloc\n",
285 return ch_malloc(size
);
287 newptr
= sh
->sh_last
;
288 *newptr
++ = size
- sizeof(ber_len_t
);
289 sh
->sh_last
= (char *) sh
->sh_last
+ size
;
290 return( (void *)newptr
);
292 size_shift
= size
- 1;
295 } while (size_shift
>>= 1);
300 } while (pad_shift
>>= 1);
302 for (i
= order
; i
<= sh
->sh_maxorder
&&
303 LDAP_LIST_EMPTY(&sh
->sh_free
[i
-order_start
]); i
++);
306 so_new
= LDAP_LIST_FIRST(&sh
->sh_free
[i
-order_start
]);
307 LDAP_LIST_REMOVE(so_new
, so_link
);
308 ptr
= so_new
->so_ptr
;
309 diff
= (unsigned long)((char*)ptr
-
310 (char*)sh
->sh_base
) >> (order
+ 1);
311 sh
->sh_map
[order
-order_start
][diff
>>3] |= (1 << (diff
& 0x7));
312 *ptr
++ = size
- sizeof(ber_len_t
);
313 LDAP_LIST_INSERT_HEAD(&sh
->sh_sopool
, so_new
, so_link
);
315 } else if (i
<= sh
->sh_maxorder
) {
316 for (j
= i
; j
> order
; j
--) {
317 so_left
= LDAP_LIST_FIRST(&sh
->sh_free
[j
-order_start
]);
318 LDAP_LIST_REMOVE(so_left
, so_link
);
319 if (LDAP_LIST_EMPTY(&sh
->sh_sopool
)) {
320 slap_replenish_sopool(sh
);
322 so_right
= LDAP_LIST_FIRST(&sh
->sh_sopool
);
323 LDAP_LIST_REMOVE(so_right
, so_link
);
324 so_right
->so_ptr
= (void *)((char *)so_left
->so_ptr
+ (1 << j
));
325 if (j
== order
+ 1) {
326 ptr
= so_left
->so_ptr
;
327 diff
= (unsigned long)((char*)ptr
-
328 (char*)sh
->sh_base
) >> (order
+1);
329 sh
->sh_map
[order
-order_start
][diff
>>3] |=
331 *ptr
++ = size
- sizeof(ber_len_t
);
332 LDAP_LIST_INSERT_HEAD(
333 &sh
->sh_free
[j
-1-order_start
], so_right
, so_link
);
334 LDAP_LIST_INSERT_HEAD(&sh
->sh_sopool
, so_left
, so_link
);
337 LDAP_LIST_INSERT_HEAD(
338 &sh
->sh_free
[j
-1-order_start
], so_right
, so_link
);
339 LDAP_LIST_INSERT_HEAD(
340 &sh
->sh_free
[j
-1-order_start
], so_left
, so_link
);
344 Debug( LDAP_DEBUG_TRACE
,
345 "slap_sl_malloc of %lu bytes failed, using ch_malloc\n",
347 return (void*)ch_malloc(size
);
351 /* FIXME: missing return; guessing... */
356 slap_sl_calloc( ber_len_t n
, ber_len_t size
, void *ctx
)
360 newptr
= slap_sl_malloc( n
*size
, ctx
);
362 memset( newptr
, 0, n
*size
);
368 slap_sl_realloc(void *ptr
, ber_len_t size
, void *ctx
)
370 struct slab_heap
*sh
= ctx
;
371 int pad
= 2*sizeof(int) -1;
372 ber_len_t
*p
= (ber_len_t
*)ptr
, *newptr
;
375 return slap_sl_malloc(size
, ctx
);
377 #ifdef SLAP_NO_SL_MALLOC
378 return ber_memrealloc_x( ptr
, size
, NULL
);
381 /* Not our memory? */
382 if (!sh
|| ptr
< sh
->sh_base
|| ptr
>= sh
->sh_end
) {
383 /* duplicate of realloc behavior, oh well */
384 newptr
= ber_memrealloc_x(ptr
, size
, NULL
);
388 Debug(LDAP_DEBUG_ANY
, "ch_realloc of %lu bytes failed\n",
391 exit( EXIT_FAILURE
);
395 slap_sl_free(ptr
, ctx
);
400 /* round up to doubleword boundary */
401 size
+= pad
+ sizeof( ber_len_t
);
404 /* Never shrink blocks */
408 /* If reallocing the last block, we can grow it */
409 } else if ((char *)ptr
+ p
[-1] == sh
->sh_last
&&
410 (char *)ptr
+ size
< (char *)sh
->sh_end
) {
412 sh
->sh_last
= (char *)sh
->sh_last
+ size
- p
[-1];
415 /* Nowhere to grow, need to alloc and copy */
417 newptr
= slap_sl_malloc(size
, ctx
);
418 AC_MEMCPY(newptr
, ptr
, p
[-1]);
424 newptr2
= slap_sl_malloc(size
, ctx
);
426 AC_MEMCPY(newptr2
, ptr
, size
);
428 AC_MEMCPY(newptr2
, ptr
, p
[-1]);
430 slap_sl_free(ptr
, ctx
);
436 slap_sl_free(void *ptr
, void *ctx
)
438 struct slab_heap
*sh
= ctx
;
439 int size
, size_shift
, order_size
;
440 int pad
= 2*sizeof(int)-1, pad_shift
;
441 ber_len_t
*p
= (ber_len_t
*)ptr
, *tmpp
;
442 int order_start
= -1, order
= -1;
443 struct slab_object
*so
;
450 #ifdef SLAP_NO_SL_MALLOC
451 ber_memfree_x( ptr
, NULL
);
455 if (!sh
|| ptr
< sh
->sh_base
|| ptr
>= sh
->sh_end
) {
456 ber_memfree_x(ptr
, NULL
);
457 } else if (sh
->sh_stack
&& (char *)ptr
+ p
[-1] == sh
->sh_last
) {
460 } else if (!sh
->sh_stack
) {
462 size_shift
= size
+ sizeof(ber_len_t
) - 1;
465 } while (size_shift
>>= 1);
470 } while (pad_shift
>>= 1);
472 for (i
= order
, tmpp
= p
; i
<= sh
->sh_maxorder
; i
++) {
473 order_size
= 1 << (i
+1);
474 diff
= (unsigned long)((char*)tmpp
- (char*)sh
->sh_base
) >> (i
+1);
475 sh
->sh_map
[i
-order_start
][diff
>>3] &= (~(1 << (diff
& 0x7)));
476 if (diff
== ((diff
>>1)<<1)) {
477 if (!(sh
->sh_map
[i
-order_start
][(diff
+1)>>3] &
478 (1<<((diff
+1)&0x7)))) {
479 so
= LDAP_LIST_FIRST(&sh
->sh_free
[i
-order_start
]);
481 if ((char*)so
->so_ptr
== (char*)tmpp
) {
482 LDAP_LIST_REMOVE( so
, so_link
);
483 } else if ((char*)so
->so_ptr
==
484 (char*)tmpp
+ order_size
) {
485 LDAP_LIST_REMOVE(so
, so_link
);
488 so
= LDAP_LIST_NEXT(so
, so_link
);
491 if (i
< sh
->sh_maxorder
) {
494 LDAP_LIST_INSERT_HEAD(&sh
->sh_free
[i
-order_start
+1],
499 if (LDAP_LIST_EMPTY(&sh
->sh_sopool
)) {
500 slap_replenish_sopool(sh
);
502 so
= LDAP_LIST_FIRST(&sh
->sh_sopool
);
503 LDAP_LIST_REMOVE(so
, so_link
);
505 LDAP_LIST_INSERT_HEAD(&sh
->sh_free
[i
-order_start
],
509 Debug(LDAP_DEBUG_TRACE
, "slap_sl_free: "
510 "free object not found while bit is clear.\n",
517 if (LDAP_LIST_EMPTY(&sh
->sh_sopool
)) {
518 slap_replenish_sopool(sh
);
520 so
= LDAP_LIST_FIRST(&sh
->sh_sopool
);
521 LDAP_LIST_REMOVE(so
, so_link
);
523 LDAP_LIST_INSERT_HEAD(&sh
->sh_free
[i
-order_start
],
529 if (!(sh
->sh_map
[i
-order_start
][(diff
-1)>>3] &
530 (1<<((diff
-1)&0x7)))) {
531 so
= LDAP_LIST_FIRST(&sh
->sh_free
[i
-order_start
]);
533 if ((char*)so
->so_ptr
== (char*)tmpp
) {
534 LDAP_LIST_REMOVE(so
, so_link
);
535 } else if ((char*)tmpp
== (char *)so
->so_ptr
+ order_size
) {
536 LDAP_LIST_REMOVE(so
, so_link
);
540 so
= LDAP_LIST_NEXT(so
, so_link
);
543 if (i
< sh
->sh_maxorder
) {
545 LDAP_LIST_INSERT_HEAD(&sh
->sh_free
[i
-order_start
+1], so
, so_link
);
549 if (LDAP_LIST_EMPTY(&sh
->sh_sopool
)) {
550 slap_replenish_sopool(sh
);
552 so
= LDAP_LIST_FIRST(&sh
->sh_sopool
);
553 LDAP_LIST_REMOVE(so
, so_link
);
555 LDAP_LIST_INSERT_HEAD(&sh
->sh_free
[i
-order_start
],
559 Debug(LDAP_DEBUG_TRACE
, "slap_sl_free: "
560 "free object not found while bit is clear.\n",
567 if (LDAP_LIST_EMPTY(&sh
->sh_sopool
)) {
568 slap_replenish_sopool(sh
);
570 so
= LDAP_LIST_FIRST(&sh
->sh_sopool
);
571 LDAP_LIST_REMOVE(so
, so_link
);
573 LDAP_LIST_INSERT_HEAD(&sh
->sh_free
[i
-order_start
],
584 slap_sl_context( void *ptr
)
586 struct slab_heap
*sh
;
589 if ( slapMode
& SLAP_TOOL_MODE
) return NULL
;
594 ctx
= ldap_pvt_thread_pool_context();
597 ldap_pvt_thread_pool_getkey(
598 ctx
, (void *)slap_sl_mem_init
, &sh_tmp
, NULL
);
602 if (sh
&& ptr
>= sh
->sh_base
&& ptr
<= sh
->sh_end
) {
608 static struct slab_object
*
609 slap_replenish_sopool(
613 struct slab_object
*so_block
;
616 so_block
= (struct slab_object
*)ch_malloc(
617 SLAP_SLAB_SOBLOCK
* sizeof(struct slab_object
));
619 if ( so_block
== NULL
) {
623 so_block
[0].so_blockhead
= 1;
624 LDAP_LIST_INSERT_HEAD(&sh
->sh_sopool
, &so_block
[0], so_link
);
625 for (i
= 1; i
< SLAP_SLAB_SOBLOCK
; i
++) {
626 so_block
[i
].so_blockhead
= 0;
627 LDAP_LIST_INSERT_HEAD(&sh
->sh_sopool
, &so_block
[i
], so_link
);
635 print_slheap(int level
, void *ctx
)
637 struct slab_heap
*sh
= ctx
;
638 int order_start
= -1;
639 int pad
= 2*sizeof(int)-1, pad_shift
;
640 struct slab_object
*so
;
644 Debug(level
, "NULL memctx\n", 0, 0, 0);
651 } while (pad_shift
>>= 1);
653 Debug(level
, "sh->sh_maxorder=%d\n", sh
->sh_maxorder
, 0, 0);
655 for (i
= order_start
; i
<= sh
->sh_maxorder
; i
++) {
657 Debug(level
, "order=%d\n", i
, 0, 0);
658 for (j
= 0; j
< (1<<(sh
->sh_maxorder
-i
))/8; j
++) {
659 Debug(level
, "%02x ", sh
->sh_map
[i
-order_start
][j
], 0, 0);
663 Debug(level
, "%02x ", sh
->sh_map
[i
-order_start
][0], 0, 0);
665 Debug(level
, "\n", 0, 0, 0);
666 Debug(level
, "free list:\n", 0, 0, 0);
667 so
= LDAP_LIST_FIRST(&sh
->sh_free
[i
-order_start
]);
669 Debug(level
, "%lx\n", (unsigned long) so
->so_ptr
, 0, 0);
670 so
= LDAP_LIST_NEXT(so
, so_link
);