xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/sl_malloc.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
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