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