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