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