Home | History | Annotate | Line # | Download | only in slapd
sl_malloc.c revision 1.1.1.6
      1 /*	$NetBSD: sl_malloc.c,v 1.1.1.6 2018/02/06 01:53:15 christos Exp $	*/
      2 
      3 /* sl_malloc.c - malloc routines using a per-thread slab */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2003-2017 The OpenLDAP Foundation.
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted only as authorized by the OpenLDAP
     12  * Public License.
     13  *
     14  * A copy of this license is available in the file LICENSE in the
     15  * top-level directory of the distribution or, alternatively, at
     16  * <http://www.OpenLDAP.org/license.html>.
     17  */
     18 
     19 #include <sys/cdefs.h>
     20 __RCSID("$NetBSD: sl_malloc.c,v 1.1.1.6 2018/02/06 01:53:15 christos Exp $");
     21 
     22 #include "portable.h"
     23 
     24 #include <stdio.h>
     25 #include <ac/string.h>
     26 
     27 #include "slap.h"
     28 
     29 #ifdef USE_VALGRIND
     30 /* Get debugging help from Valgrind */
     31 #include <valgrind/memcheck.h>
     32 #define	VGMEMP_MARK(m,s)	VALGRIND_MAKE_MEM_NOACCESS(m,s)
     33 #define VGMEMP_CREATE(h,r,z)	VALGRIND_CREATE_MEMPOOL(h,r,z)
     34 #define VGMEMP_TRIM(h,a,s)	VALGRIND_MEMPOOL_TRIM(h,a,s)
     35 #define VGMEMP_ALLOC(h,a,s)	VALGRIND_MEMPOOL_ALLOC(h,a,s)
     36 #define VGMEMP_CHANGE(h,a,b,s)	VALGRIND_MEMPOOL_CHANGE(h,a,b,s)
     37 #else
     38 #define	VGMEMP_MARK(m,s)
     39 #define VGMEMP_CREATE(h,r,z)
     40 #define VGMEMP_TRIM(h,a,s)
     41 #define VGMEMP_ALLOC(h,a,s)
     42 #define VGMEMP_CHANGE(h,a,b,s)
     43 #endif
     44 
     45 /*
     46  * This allocator returns temporary memory from a slab in a given memory
     47  * context, aligned on a 2-int boundary.  It cannot be used for data
     48  * which will outlive the task allocating it.
     49  *
     50  * A new memory context attaches to the creator's thread context, if any.
     51  * Threads cannot use other threads' memory contexts; there are no locks.
     52  *
     53  * The caller of slap_sl_malloc, usually a thread pool task, must
     54  * slap_sl_free the memory before finishing: New tasks reuse the context
     55  * and normally reset it, reclaiming memory left over from last task.
     56  *
     57  * The allocator helps memory fragmentation, speed and memory leaks.
     58  * It is not (yet) reliable as a garbage collector:
     59  *
     60  * It falls back to context NULL - plain ber_memalloc() - when the
     61  * context's slab is full.  A reset does not reclaim such memory.
     62  * Conversely, free/realloc of data not from the given context assumes
     63  * context NULL.  The data must not belong to another memory context.
     64  *
     65  * Code which has lost track of the current memory context can try
     66  * slap_sl_context() or ch_malloc.c:ch_free/ch_realloc().
     67  *
     68  * Allocations cannot yet return failure.  Like ch_malloc, they succeed
     69  * or abort slapd.  This will change, do fix code which assumes success.
     70  */
     71 
     72 /*
     73  * The stack-based allocator stores (ber_len_t)sizeof(head+block) at
     74  * allocated blocks' head - and in freed blocks also at the tail, marked
     75  * by ORing *next* block's head with 1.  Freed blocks are only reclaimed
     76  * from the last block forward.  This is fast, but when a block is never
     77  * freed, older blocks will not be reclaimed until the slab is reset...
     78  */
     79 
     80 #ifdef SLAP_NO_SL_MALLOC /* Useful with memory debuggers like Valgrind */
     81 enum { No_sl_malloc = 1 };
     82 #else
     83 enum { No_sl_malloc = 0 };
     84 #endif
     85 
     86 #define SLAP_SLAB_SOBLOCK 64
     87 
     88 struct slab_object {
     89     void *so_ptr;
     90 	int so_blockhead;
     91     LDAP_LIST_ENTRY(slab_object) so_link;
     92 };
     93 
     94 struct slab_heap {
     95     void *sh_base;
     96     void *sh_last;
     97     void *sh_end;
     98 	int sh_stack;
     99 	int sh_maxorder;
    100     unsigned char **sh_map;
    101     LDAP_LIST_HEAD(sh_freelist, slab_object) *sh_free;
    102 	LDAP_LIST_HEAD(sh_so, slab_object) sh_sopool;
    103 };
    104 
    105 enum {
    106 	Align = sizeof(ber_len_t) > 2*sizeof(int)
    107 		? sizeof(ber_len_t) : 2*sizeof(int),
    108 	Align_log2 = 1 + (Align>2) + (Align>4) + (Align>8) + (Align>16),
    109 	order_start = Align_log2 - 1,
    110 	pad = Align - 1
    111 };
    112 
    113 static struct slab_object * slap_replenish_sopool(struct slab_heap* sh);
    114 #ifdef SLAPD_UNUSED
    115 static void print_slheap(int level, void *ctx);
    116 #endif
    117 
    118 /* Keep memory context in a thread-local var, or in a global when no threads */
    119 #ifdef NO_THREADS
    120 static struct slab_heap *slheap;
    121 # define SET_MEMCTX(thrctx, memctx, sfree)	((void) (slheap = (memctx)))
    122 # define GET_MEMCTX(thrctx, memctxp)		(*(memctxp) = slheap)
    123 #else
    124 # define memctx_key ((void *) slap_sl_mem_init)
    125 # define SET_MEMCTX(thrctx, memctx, kfree) \
    126 	ldap_pvt_thread_pool_setkey(thrctx,memctx_key, memctx,kfree, NULL,NULL)
    127 # define GET_MEMCTX(thrctx, memctxp) \
    128 	((void) (*(memctxp) = NULL), \
    129 	 (void) ldap_pvt_thread_pool_getkey(thrctx,memctx_key, memctxp,NULL), \
    130 	 *(memctxp))
    131 #endif /* NO_THREADS */
    132 
    133 
    134 /* Destroy the context, or if key==NULL clean it up for reuse. */
    135 void
    136 slap_sl_mem_destroy(
    137 	void *key,
    138 	void *data
    139 )
    140 {
    141 	struct slab_heap *sh = data;
    142 	struct slab_object *so;
    143 	int i;
    144 
    145 	if (!sh->sh_stack) {
    146 		for (i = 0; i <= sh->sh_maxorder - order_start; i++) {
    147 			so = LDAP_LIST_FIRST(&sh->sh_free[i]);
    148 			while (so) {
    149 				struct slab_object *so_tmp = so;
    150 				so = LDAP_LIST_NEXT(so, so_link);
    151 				LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, so_tmp, so_link);
    152 			}
    153 			ch_free(sh->sh_map[i]);
    154 		}
    155 		ch_free(sh->sh_free);
    156 		ch_free(sh->sh_map);
    157 
    158 		so = LDAP_LIST_FIRST(&sh->sh_sopool);
    159 		while (so) {
    160 			struct slab_object *so_tmp = so;
    161 			so = LDAP_LIST_NEXT(so, so_link);
    162 			if (!so_tmp->so_blockhead) {
    163 				LDAP_LIST_REMOVE(so_tmp, so_link);
    164 			}
    165 		}
    166 		so = LDAP_LIST_FIRST(&sh->sh_sopool);
    167 		while (so) {
    168 			struct slab_object *so_tmp = so;
    169 			so = LDAP_LIST_NEXT(so, so_link);
    170 			ch_free(so_tmp);
    171 		}
    172 	}
    173 
    174 	if (key != NULL) {
    175 		ber_memfree_x(sh->sh_base, NULL);
    176 		ber_memfree_x(sh, NULL);
    177 	}
    178 }
    179 
    180 BerMemoryFunctions slap_sl_mfuncs =
    181 	{ slap_sl_malloc, slap_sl_calloc, slap_sl_realloc, slap_sl_free };
    182 
    183 void
    184 slap_sl_mem_init()
    185 {
    186 	assert( Align == 1 << Align_log2 );
    187 
    188 	ber_set_option( NULL, LBER_OPT_MEMORY_FNS, &slap_sl_mfuncs );
    189 }
    190 
    191 /* Create, reset or just return the memory context of the current thread. */
    192 void *
    193 slap_sl_mem_create(
    194 	ber_len_t size,
    195 	int stack,
    196 	void *thrctx,
    197 	int new
    198 )
    199 {
    200 	void *memctx;
    201 	struct slab_heap *sh;
    202 	ber_len_t size_shift;
    203 	struct slab_object *so;
    204 	char *base, *newptr;
    205 	enum { Base_offset = (unsigned) -sizeof(ber_len_t) % Align };
    206 
    207 	sh = GET_MEMCTX(thrctx, &memctx);
    208 	if ( sh && !new )
    209 		return sh;
    210 
    211 	/* Round up to doubleword boundary, then make room for initial
    212 	 * padding, preserving expected available size for pool version */
    213 	size = ((size + Align-1) & -Align) + Base_offset;
    214 
    215 	if (!sh) {
    216 		sh = ch_malloc(sizeof(struct slab_heap));
    217 		base = ch_malloc(size);
    218 		SET_MEMCTX(thrctx, sh, slap_sl_mem_destroy);
    219 		VGMEMP_MARK(base, size);
    220 		VGMEMP_CREATE(sh, 0, 0);
    221 	} else {
    222 		slap_sl_mem_destroy(NULL, sh);
    223 		base = sh->sh_base;
    224 		if (size > (ber_len_t) ((char *) sh->sh_end - base)) {
    225 			newptr = ch_realloc(base, size);
    226 			if ( newptr == NULL ) return NULL;
    227 			VGMEMP_CHANGE(sh, base, newptr, size);
    228 			base = newptr;
    229 		}
    230 		VGMEMP_TRIM(sh, base, 0);
    231 	}
    232 	sh->sh_base = base;
    233 	sh->sh_end = base + size;
    234 
    235 	/* Align (base + head of first block) == first returned block */
    236 	base += Base_offset;
    237 	size -= Base_offset;
    238 
    239 	sh->sh_stack = stack;
    240 	if (stack) {
    241 		sh->sh_last = base;
    242 
    243 	} else {
    244 		int i, order = -1, order_end = -1;
    245 
    246 		size_shift = size - 1;
    247 		do {
    248 			order_end++;
    249 		} while (size_shift >>= 1);
    250 		order = order_end - order_start + 1;
    251 		sh->sh_maxorder = order_end;
    252 
    253 		sh->sh_free = (struct sh_freelist *)
    254 						ch_malloc(order * sizeof(struct sh_freelist));
    255 		for (i = 0; i < order; i++) {
    256 			LDAP_LIST_INIT(&sh->sh_free[i]);
    257 		}
    258 
    259 		LDAP_LIST_INIT(&sh->sh_sopool);
    260 
    261 		if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
    262 			slap_replenish_sopool(sh);
    263 		}
    264 		so = LDAP_LIST_FIRST(&sh->sh_sopool);
    265 		LDAP_LIST_REMOVE(so, so_link);
    266 		so->so_ptr = base;
    267 
    268 		LDAP_LIST_INSERT_HEAD(&sh->sh_free[order-1], so, so_link);
    269 
    270 		sh->sh_map = (unsigned char **)
    271 					ch_malloc(order * sizeof(unsigned char *));
    272 		for (i = 0; i < order; i++) {
    273 			int shiftamt = order_start + 1 + i;
    274 			int nummaps = size >> shiftamt;
    275 			assert(nummaps);
    276 			nummaps >>= 3;
    277 			if (!nummaps) nummaps = 1;
    278 			sh->sh_map[i] = (unsigned char *) ch_malloc(nummaps);
    279 			memset(sh->sh_map[i], 0, nummaps);
    280 		}
    281 	}
    282 
    283 	return sh;
    284 }
    285 
    286 /*
    287  * Assign memory context to thread context. Use NULL to detach
    288  * current memory context from thread. Future users must
    289  * know the context, since ch_free/slap_sl_context() cannot find it.
    290  */
    291 void
    292 slap_sl_mem_setctx(
    293 	void *thrctx,
    294 	void *memctx
    295 )
    296 {
    297 	SET_MEMCTX(thrctx, memctx, slap_sl_mem_destroy);
    298 }
    299 
    300 void *
    301 slap_sl_malloc(
    302     ber_len_t	size,
    303     void *ctx
    304 )
    305 {
    306 	struct slab_heap *sh = ctx;
    307 	ber_len_t *ptr, *newptr;
    308 
    309 	/* ber_set_option calls us like this */
    310 	if (No_sl_malloc || !ctx) {
    311 		newptr = ber_memalloc_x( size, NULL );
    312 		if ( newptr ) return newptr;
    313 		Debug(LDAP_DEBUG_ANY, "slap_sl_malloc of %lu bytes failed\n",
    314 			(unsigned long) size, 0, 0);
    315 		assert( 0 );
    316 		exit( EXIT_FAILURE );
    317 	}
    318 
    319 	/* Add room for head, ensure room for tail when freed, and
    320 	 * round up to doubleword boundary. */
    321 	size = (size + sizeof(ber_len_t) + Align-1 + !size) & -Align;
    322 
    323 	if (sh->sh_stack) {
    324 		if (size < (ber_len_t) ((char *) sh->sh_end - (char *) sh->sh_last)) {
    325 			newptr = sh->sh_last;
    326 			sh->sh_last = (char *) sh->sh_last + size;
    327 			VGMEMP_ALLOC(sh, newptr, size);
    328 			*newptr++ = size;
    329 			return( (void *)newptr );
    330 		}
    331 
    332 		size -= sizeof(ber_len_t);
    333 
    334 	} else {
    335 		struct slab_object *so_new, *so_left, *so_right;
    336 		ber_len_t size_shift;
    337 		unsigned long diff;
    338 		int i, j, order = -1;
    339 
    340 		size_shift = size - 1;
    341 		do {
    342 			order++;
    343 		} while (size_shift >>= 1);
    344 
    345 		size -= sizeof(ber_len_t);
    346 
    347 		for (i = order; i <= sh->sh_maxorder &&
    348 				LDAP_LIST_EMPTY(&sh->sh_free[i-order_start]); i++);
    349 
    350 		if (i == order) {
    351 			so_new = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
    352 			LDAP_LIST_REMOVE(so_new, so_link);
    353 			ptr = so_new->so_ptr;
    354 			diff = (unsigned long)((char*)ptr -
    355 					(char*)sh->sh_base) >> (order + 1);
    356 			sh->sh_map[order-order_start][diff>>3] |= (1 << (diff & 0x7));
    357 			*ptr++ = size;
    358 			LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, so_new, so_link);
    359 			return((void*)ptr);
    360 		} else if (i <= sh->sh_maxorder) {
    361 			for (j = i; j > order; j--) {
    362 				so_left = LDAP_LIST_FIRST(&sh->sh_free[j-order_start]);
    363 				LDAP_LIST_REMOVE(so_left, so_link);
    364 				if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
    365 					slap_replenish_sopool(sh);
    366 				}
    367 				so_right = LDAP_LIST_FIRST(&sh->sh_sopool);
    368 				LDAP_LIST_REMOVE(so_right, so_link);
    369 				so_right->so_ptr = (void *)((char *)so_left->so_ptr + (1 << j));
    370 				if (j == order + 1) {
    371 					ptr = so_left->so_ptr;
    372 					diff = (unsigned long)((char*)ptr -
    373 							(char*)sh->sh_base) >> (order+1);
    374 					sh->sh_map[order-order_start][diff>>3] |=
    375 							(1 << (diff & 0x7));
    376 					*ptr++ = size;
    377 					LDAP_LIST_INSERT_HEAD(
    378 							&sh->sh_free[j-1-order_start], so_right, so_link);
    379 					LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, so_left, so_link);
    380 					return((void*)ptr);
    381 				} else {
    382 					LDAP_LIST_INSERT_HEAD(
    383 							&sh->sh_free[j-1-order_start], so_right, so_link);
    384 					LDAP_LIST_INSERT_HEAD(
    385 							&sh->sh_free[j-1-order_start], so_left, so_link);
    386 				}
    387 			}
    388 		}
    389 		/* FIXME: missing return; guessing we failed... */
    390 	}
    391 
    392 	Debug(LDAP_DEBUG_TRACE,
    393 		"sl_malloc %lu: ch_malloc\n",
    394 		(unsigned long) size, 0, 0);
    395 	return ch_malloc(size);
    396 }
    397 
    398 #define LIM_SQRT(t) /* some value < sqrt(max value of unsigned type t) */ \
    399 	((0UL|(t)-1) >>31>>31 > 1 ? ((t)1 <<32) - 1 : \
    400 	 (0UL|(t)-1) >>31 ? 65535U : (0UL|(t)-1) >>15 ? 255U : 15U)
    401 
    402 void *
    403 slap_sl_calloc( ber_len_t n, ber_len_t size, void *ctx )
    404 {
    405 	void *newptr;
    406 	ber_len_t total = n * size;
    407 
    408 	/* The sqrt test is a slight optimization: often avoids the division */
    409 	if ((n | size) <= LIM_SQRT(ber_len_t) || n == 0 || total/n == size) {
    410 		newptr = slap_sl_malloc( total, ctx );
    411 		memset( newptr, 0, n*size );
    412 	} else {
    413 		Debug(LDAP_DEBUG_ANY, "slap_sl_calloc(%lu,%lu) out of range\n",
    414 			(unsigned long) n, (unsigned long) size, 0);
    415 		assert(0);
    416 		exit(EXIT_FAILURE);
    417 	}
    418 	return newptr;
    419 }
    420 
    421 void *
    422 slap_sl_realloc(void *ptr, ber_len_t size, void *ctx)
    423 {
    424 	struct slab_heap *sh = ctx;
    425 	ber_len_t oldsize, *p = (ber_len_t *) ptr, *nextp;
    426 	void *newptr;
    427 
    428 	if (ptr == NULL)
    429 		return slap_sl_malloc(size, ctx);
    430 
    431 	/* Not our memory? */
    432 	if (No_sl_malloc || !sh || ptr < sh->sh_base || ptr >= sh->sh_end) {
    433 		/* Like ch_realloc(), except not trying a new context */
    434 		newptr = ber_memrealloc_x(ptr, size, NULL);
    435 		if (newptr) {
    436 			return newptr;
    437 		}
    438 		Debug(LDAP_DEBUG_ANY, "slap_sl_realloc of %lu bytes failed\n",
    439 			(unsigned long) size, 0, 0);
    440 		assert(0);
    441 		exit( EXIT_FAILURE );
    442 	}
    443 
    444 	if (size == 0) {
    445 		slap_sl_free(ptr, ctx);
    446 		return NULL;
    447 	}
    448 
    449 	oldsize = p[-1];
    450 
    451 	if (sh->sh_stack) {
    452 		/* Add room for head, round up to doubleword boundary */
    453 		size = (size + sizeof(ber_len_t) + Align-1) & -Align;
    454 
    455 		p--;
    456 
    457 		/* Never shrink blocks */
    458 		if (size <= oldsize) {
    459 			return ptr;
    460 		}
    461 
    462 		oldsize &= -2;
    463 		nextp = (ber_len_t *) ((char *) p + oldsize);
    464 
    465 		/* If reallocing the last block, try to grow it */
    466 		if (nextp == sh->sh_last) {
    467 			if (size < (ber_len_t) ((char *) sh->sh_end - (char *) p)) {
    468 				sh->sh_last = (char *) p + size;
    469 				p[0] = (p[0] & 1) | size;
    470 				return ptr;
    471 			}
    472 
    473 		/* Nowhere to grow, need to alloc and copy */
    474 		} else {
    475 			/* Slight optimization of the final realloc variant */
    476 			newptr = slap_sl_malloc(size-sizeof(ber_len_t), ctx);
    477 			AC_MEMCPY(newptr, ptr, oldsize-sizeof(ber_len_t));
    478 			/* Not last block, can just mark old region as free */
    479 			nextp[-1] = oldsize;
    480 			nextp[0] |= 1;
    481 			return newptr;
    482 		}
    483 
    484 		size -= sizeof(ber_len_t);
    485 		oldsize -= sizeof(ber_len_t);
    486 
    487 	} else if (oldsize > size) {
    488 		oldsize = size;
    489 	}
    490 
    491 	newptr = slap_sl_malloc(size, ctx);
    492 	AC_MEMCPY(newptr, ptr, oldsize);
    493 	slap_sl_free(ptr, ctx);
    494 	return newptr;
    495 }
    496 
    497 void
    498 slap_sl_free(void *ptr, void *ctx)
    499 {
    500 	struct slab_heap *sh = ctx;
    501 	ber_len_t size;
    502 	ber_len_t *p = ptr, *nextp, *tmpp;
    503 
    504 	if (!ptr)
    505 		return;
    506 
    507 	if (No_sl_malloc || !sh || ptr < sh->sh_base || ptr >= sh->sh_end) {
    508 		ber_memfree_x(ptr, NULL);
    509 		return;
    510 	}
    511 
    512 	size = *(--p);
    513 
    514 	if (sh->sh_stack) {
    515 		size &= -2;
    516 		nextp = (ber_len_t *) ((char *) p + size);
    517 		if (sh->sh_last != nextp) {
    518 			/* Mark it free: tail = size, head of next block |= 1 */
    519 			nextp[-1] = size;
    520 			nextp[0] |= 1;
    521 			/* We can't tell Valgrind about it yet, because we
    522 			 * still need read/write access to this block for
    523 			 * when we eventually get to reclaim it.
    524 			 */
    525 		} else {
    526 			/* Reclaim freed block(s) off tail */
    527 			while (*p & 1) {
    528 				p = (ber_len_t *) ((char *) p - p[-1]);
    529 			}
    530 			sh->sh_last = p;
    531 			VGMEMP_TRIM(sh, sh->sh_base,
    532 				(char *) sh->sh_last - (char *) sh->sh_base);
    533 		}
    534 
    535 	} else {
    536 		int size_shift, order_size;
    537 		struct slab_object *so;
    538 		unsigned long diff;
    539 		int i, inserted = 0, order = -1;
    540 
    541 		size_shift = size + sizeof(ber_len_t) - 1;
    542 		do {
    543 			order++;
    544 		} while (size_shift >>= 1);
    545 
    546 		for (i = order, tmpp = p; i <= sh->sh_maxorder; i++) {
    547 			order_size = 1 << (i+1);
    548 			diff = (unsigned long)((char*)tmpp - (char*)sh->sh_base) >> (i+1);
    549 			sh->sh_map[i-order_start][diff>>3] &= (~(1 << (diff & 0x7)));
    550 			if (diff == ((diff>>1)<<1)) {
    551 				if (!(sh->sh_map[i-order_start][(diff+1)>>3] &
    552 						(1<<((diff+1)&0x7)))) {
    553 					so = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
    554 					while (so) {
    555 						if ((char*)so->so_ptr == (char*)tmpp) {
    556 							LDAP_LIST_REMOVE( so, so_link );
    557 						} else if ((char*)so->so_ptr ==
    558 								(char*)tmpp + order_size) {
    559 							LDAP_LIST_REMOVE(so, so_link);
    560 							break;
    561 						}
    562 						so = LDAP_LIST_NEXT(so, so_link);
    563 					}
    564 					if (so) {
    565 						if (i < sh->sh_maxorder) {
    566 							inserted = 1;
    567 							so->so_ptr = tmpp;
    568 							LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start+1],
    569 									so, so_link);
    570 						}
    571 						continue;
    572 					} else {
    573 						if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
    574 							slap_replenish_sopool(sh);
    575 						}
    576 						so = LDAP_LIST_FIRST(&sh->sh_sopool);
    577 						LDAP_LIST_REMOVE(so, so_link);
    578 						so->so_ptr = tmpp;
    579 						LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
    580 								so, so_link);
    581 						break;
    582 
    583 						Debug(LDAP_DEBUG_TRACE, "slap_sl_free: "
    584 							"free object not found while bit is clear.\n",
    585 							0, 0, 0);
    586 						assert(so != NULL);
    587 
    588 					}
    589 				} else {
    590 					if (!inserted) {
    591 						if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
    592 							slap_replenish_sopool(sh);
    593 						}
    594 						so = LDAP_LIST_FIRST(&sh->sh_sopool);
    595 						LDAP_LIST_REMOVE(so, so_link);
    596 						so->so_ptr = tmpp;
    597 						LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
    598 								so, so_link);
    599 					}
    600 					break;
    601 				}
    602 			} else {
    603 				if (!(sh->sh_map[i-order_start][(diff-1)>>3] &
    604 						(1<<((diff-1)&0x7)))) {
    605 					so = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
    606 					while (so) {
    607 						if ((char*)so->so_ptr == (char*)tmpp) {
    608 							LDAP_LIST_REMOVE(so, so_link);
    609 						} else if ((char*)tmpp == (char *)so->so_ptr + order_size) {
    610 							LDAP_LIST_REMOVE(so, so_link);
    611 							tmpp = so->so_ptr;
    612 							break;
    613 						}
    614 						so = LDAP_LIST_NEXT(so, so_link);
    615 					}
    616 					if (so) {
    617 						if (i < sh->sh_maxorder) {
    618 							inserted = 1;
    619 							LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start+1],									so, so_link);
    620 							continue;
    621 						}
    622 					} else {
    623 						if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
    624 							slap_replenish_sopool(sh);
    625 						}
    626 						so = LDAP_LIST_FIRST(&sh->sh_sopool);
    627 						LDAP_LIST_REMOVE(so, so_link);
    628 						so->so_ptr = tmpp;
    629 						LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
    630 								so, so_link);
    631 						break;
    632 
    633 						Debug(LDAP_DEBUG_TRACE, "slap_sl_free: "
    634 							"free object not found while bit is clear.\n",
    635 							0, 0, 0 );
    636 						assert(so != NULL);
    637 
    638 					}
    639 				} else {
    640 					if ( !inserted ) {
    641 						if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
    642 							slap_replenish_sopool(sh);
    643 						}
    644 						so = LDAP_LIST_FIRST(&sh->sh_sopool);
    645 						LDAP_LIST_REMOVE(so, so_link);
    646 						so->so_ptr = tmpp;
    647 						LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
    648 								so, so_link);
    649 					}
    650 					break;
    651 				}
    652 			}
    653 		}
    654 	}
    655 }
    656 
    657 /*
    658  * Return the memory context of the current thread if the given block of
    659  * memory belongs to it, otherwise return NULL.
    660  */
    661 void *
    662 slap_sl_context( void *ptr )
    663 {
    664 	void *memctx;
    665 	struct slab_heap *sh;
    666 
    667 	if ( slapMode & SLAP_TOOL_MODE ) return NULL;
    668 
    669 	sh = GET_MEMCTX(ldap_pvt_thread_pool_context(), &memctx);
    670 	if (sh && ptr >= sh->sh_base && ptr <= sh->sh_end) {
    671 		return sh;
    672 	}
    673 	return NULL;
    674 }
    675 
    676 static struct slab_object *
    677 slap_replenish_sopool(
    678     struct slab_heap* sh
    679 )
    680 {
    681     struct slab_object *so_block;
    682     int i;
    683 
    684     so_block = (struct slab_object *)ch_malloc(
    685                     SLAP_SLAB_SOBLOCK * sizeof(struct slab_object));
    686 
    687     if ( so_block == NULL ) {
    688         return NULL;
    689     }
    690 
    691     so_block[0].so_blockhead = 1;
    692     LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, &so_block[0], so_link);
    693     for (i = 1; i < SLAP_SLAB_SOBLOCK; i++) {
    694         so_block[i].so_blockhead = 0;
    695         LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, &so_block[i], so_link );
    696     }
    697 
    698     return so_block;
    699 }
    700 
    701 #ifdef SLAPD_UNUSED
    702 static void
    703 print_slheap(int level, void *ctx)
    704 {
    705 	struct slab_heap *sh = ctx;
    706 	struct slab_object *so;
    707 	int i, j, once = 0;
    708 
    709 	if (!ctx) {
    710 		Debug(level, "NULL memctx\n", 0, 0, 0);
    711 		return;
    712 	}
    713 
    714 	Debug(level, "sh->sh_maxorder=%d\n", sh->sh_maxorder, 0, 0);
    715 
    716 	for (i = order_start; i <= sh->sh_maxorder; i++) {
    717 		once = 0;
    718 		Debug(level, "order=%d\n", i, 0, 0);
    719 		for (j = 0; j < (1<<(sh->sh_maxorder-i))/8; j++) {
    720 			Debug(level, "%02x ", sh->sh_map[i-order_start][j], 0, 0);
    721 			once = 1;
    722 		}
    723 		if (!once) {
    724 			Debug(level, "%02x ", sh->sh_map[i-order_start][0], 0, 0);
    725 		}
    726 		Debug(level, "\n", 0, 0, 0);
    727 		Debug(level, "free list:\n", 0, 0, 0);
    728 		so = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
    729 		while (so) {
    730 			Debug(level, "%p\n", so->so_ptr, 0, 0);
    731 			so = LDAP_LIST_NEXT(so, so_link);
    732 		}
    733 	}
    734 }
    735 #endif
    736