]> git.sur5r.net Git - openldap/blob - servers/slapd/sl_malloc.c
ITS#8616 don't check for existing value when deleting values
[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-2018 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)
141                 return;
142
143         if (!sh->sh_stack) {
144                 for (i = 0; i <= sh->sh_maxorder - order_start; i++) {
145                         so = LDAP_LIST_FIRST(&sh->sh_free[i]);
146                         while (so) {
147                                 struct slab_object *so_tmp = so;
148                                 so = LDAP_LIST_NEXT(so, so_link);
149                                 LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, so_tmp, so_link);
150                         }
151                         ch_free(sh->sh_map[i]);
152                 }
153                 ch_free(sh->sh_free);
154                 ch_free(sh->sh_map);
155
156                 so = LDAP_LIST_FIRST(&sh->sh_sopool);
157                 while (so) {
158                         struct slab_object *so_tmp = so;
159                         so = LDAP_LIST_NEXT(so, so_link);
160                         if (!so_tmp->so_blockhead) {
161                                 LDAP_LIST_REMOVE(so_tmp, so_link);
162                         }
163                 }
164                 so = LDAP_LIST_FIRST(&sh->sh_sopool);
165                 while (so) {
166                         struct slab_object *so_tmp = so;
167                         so = LDAP_LIST_NEXT(so, so_link);
168                         ch_free(so_tmp);
169                 }
170         }
171
172         if (key != NULL) {
173                 ber_memfree_x(sh->sh_base, NULL);
174                 ber_memfree_x(sh, NULL);
175         }
176 }
177
178 BerMemoryFunctions slap_sl_mfuncs =
179         { slap_sl_malloc, slap_sl_calloc, slap_sl_realloc, slap_sl_free };
180
181 void
182 slap_sl_mem_init()
183 {
184         assert( Align == 1 << Align_log2 );
185
186         ber_set_option( NULL, LBER_OPT_MEMORY_FNS, &slap_sl_mfuncs );
187 }
188
189 /* Create, reset or just return the memory context of the current thread. */
190 void *
191 slap_sl_mem_create(
192         ber_len_t size,
193         int stack,
194         void *thrctx,
195         int new
196 )
197 {
198         void *memctx;
199         struct slab_heap *sh;
200         ber_len_t size_shift;
201         struct slab_object *so;
202         char *base, *newptr;
203         enum { Base_offset = (unsigned) -sizeof(ber_len_t) % Align };
204
205         sh = GET_MEMCTX(thrctx, &memctx);
206         if ( sh && !new )
207                 return sh;
208
209         /* Round up to doubleword boundary, then make room for initial
210          * padding, preserving expected available size for pool version */
211         size = ((size + Align-1) & -Align) + Base_offset;
212
213         if (!sh) {
214                 sh = ch_malloc(sizeof(struct slab_heap));
215                 base = ch_malloc(size);
216                 SET_MEMCTX(thrctx, sh, slap_sl_mem_destroy);
217                 VGMEMP_MARK(base, size);
218                 VGMEMP_CREATE(sh, 0, 0);
219         } else {
220                 slap_sl_mem_destroy(NULL, sh);
221                 base = sh->sh_base;
222                 if (size > (ber_len_t) ((char *) sh->sh_end - base)) {
223                         newptr = ch_realloc(base, size);
224                         if ( newptr == NULL ) return NULL;
225                         VGMEMP_CHANGE(sh, base, newptr, size);
226                         base = newptr;
227                 }
228                 VGMEMP_TRIM(sh, base, 0);
229         }
230         sh->sh_base = base;
231         sh->sh_end = base + size;
232
233         /* Align (base + head of first block) == first returned block */
234         base += Base_offset;
235         size -= Base_offset;
236
237         sh->sh_stack = stack;
238         if (stack) {
239                 sh->sh_last = base;
240
241         } else {
242                 int i, order = -1, order_end = -1;
243
244                 size_shift = size - 1;
245                 do {
246                         order_end++;
247                 } while (size_shift >>= 1);
248                 order = order_end - order_start + 1;
249                 sh->sh_maxorder = order_end;
250
251                 sh->sh_free = (struct sh_freelist *)
252                                                 ch_malloc(order * sizeof(struct sh_freelist));
253                 for (i = 0; i < order; i++) {
254                         LDAP_LIST_INIT(&sh->sh_free[i]);
255                 }
256
257                 LDAP_LIST_INIT(&sh->sh_sopool);
258
259                 if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
260                         slap_replenish_sopool(sh);
261                 }
262                 so = LDAP_LIST_FIRST(&sh->sh_sopool);
263                 LDAP_LIST_REMOVE(so, so_link);
264                 so->so_ptr = base;
265
266                 LDAP_LIST_INSERT_HEAD(&sh->sh_free[order-1], so, so_link);
267
268                 sh->sh_map = (unsigned char **)
269                                         ch_malloc(order * sizeof(unsigned char *));
270                 for (i = 0; i < order; i++) {
271                         int shiftamt = order_start + 1 + i;
272                         int nummaps = size >> shiftamt;
273                         assert(nummaps);
274                         nummaps >>= 3;
275                         if (!nummaps) nummaps = 1;
276                         sh->sh_map[i] = (unsigned char *) ch_malloc(nummaps);
277                         memset(sh->sh_map[i], 0, nummaps);
278                 }
279         }
280
281         return sh;
282 }
283
284 /*
285  * Assign memory context to thread context. Use NULL to detach
286  * current memory context from thread. Future users must
287  * know the context, since ch_free/slap_sl_context() cannot find it.
288  */
289 void
290 slap_sl_mem_setctx(
291         void *thrctx,
292         void *memctx
293 )
294 {
295         SET_MEMCTX(thrctx, memctx, slap_sl_mem_destroy);
296 }
297
298 void *
299 slap_sl_malloc(
300     ber_len_t   size,
301     void *ctx
302 )
303 {
304         struct slab_heap *sh = ctx;
305         ber_len_t *ptr, *newptr;
306
307         /* ber_set_option calls us like this */
308         if (No_sl_malloc || !ctx) {
309                 newptr = ber_memalloc_x( size, NULL );
310                 if ( newptr ) return newptr;
311                 Debug(LDAP_DEBUG_ANY, "slap_sl_malloc of %lu bytes failed\n",
312                         (unsigned long) size, 0, 0);
313                 assert( 0 );
314                 exit( EXIT_FAILURE );
315         }
316
317         /* Add room for head, ensure room for tail when freed, and
318          * round up to doubleword boundary. */
319         size = (size + sizeof(ber_len_t) + Align-1 + !size) & -Align;
320
321         if (sh->sh_stack) {
322                 if (size < (ber_len_t) ((char *) sh->sh_end - (char *) sh->sh_last)) {
323                         newptr = sh->sh_last;
324                         sh->sh_last = (char *) sh->sh_last + size;
325                         VGMEMP_ALLOC(sh, newptr, size);
326                         *newptr++ = size;
327                         return( (void *)newptr );
328                 }
329
330                 size -= sizeof(ber_len_t);
331
332         } else {
333                 struct slab_object *so_new, *so_left, *so_right;
334                 ber_len_t size_shift;
335                 unsigned long diff;
336                 int i, j, order = -1;
337
338                 size_shift = size - 1;
339                 do {
340                         order++;
341                 } while (size_shift >>= 1);
342
343                 size -= sizeof(ber_len_t);
344
345                 for (i = order; i <= sh->sh_maxorder &&
346                                 LDAP_LIST_EMPTY(&sh->sh_free[i-order_start]); i++);
347
348                 if (i == order) {
349                         so_new = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
350                         LDAP_LIST_REMOVE(so_new, so_link);
351                         ptr = so_new->so_ptr;
352                         diff = (unsigned long)((char*)ptr -
353                                         (char*)sh->sh_base) >> (order + 1);
354                         sh->sh_map[order-order_start][diff>>3] |= (1 << (diff & 0x7));
355                         *ptr++ = size;
356                         LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, so_new, so_link);
357                         return((void*)ptr);
358                 } else if (i <= sh->sh_maxorder) {
359                         for (j = i; j > order; j--) {
360                                 so_left = LDAP_LIST_FIRST(&sh->sh_free[j-order_start]);
361                                 LDAP_LIST_REMOVE(so_left, so_link);
362                                 if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
363                                         slap_replenish_sopool(sh);
364                                 }
365                                 so_right = LDAP_LIST_FIRST(&sh->sh_sopool);
366                                 LDAP_LIST_REMOVE(so_right, so_link);
367                                 so_right->so_ptr = (void *)((char *)so_left->so_ptr + (1 << j));
368                                 if (j == order + 1) {
369                                         ptr = so_left->so_ptr;
370                                         diff = (unsigned long)((char*)ptr -
371                                                         (char*)sh->sh_base) >> (order+1);
372                                         sh->sh_map[order-order_start][diff>>3] |=
373                                                         (1 << (diff & 0x7));
374                                         *ptr++ = size;
375                                         LDAP_LIST_INSERT_HEAD(
376                                                         &sh->sh_free[j-1-order_start], so_right, so_link);
377                                         LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, so_left, so_link);
378                                         return((void*)ptr);
379                                 } else {
380                                         LDAP_LIST_INSERT_HEAD(
381                                                         &sh->sh_free[j-1-order_start], so_right, so_link);
382                                         LDAP_LIST_INSERT_HEAD(
383                                                         &sh->sh_free[j-1-order_start], so_left, so_link);
384                                 }
385                         }
386                 }
387                 /* FIXME: missing return; guessing we failed... */
388         }
389
390         Debug(LDAP_DEBUG_TRACE,
391                 "sl_malloc %lu: ch_malloc\n",
392                 (unsigned long) size, 0, 0);
393         return ch_malloc(size);
394 }
395
396 #define LIM_SQRT(t) /* some value < sqrt(max value of unsigned type t) */ \
397         ((0UL|(t)-1) >>31>>31 > 1 ? ((t)1 <<32) - 1 : \
398          (0UL|(t)-1) >>31 ? 65535U : (0UL|(t)-1) >>15 ? 255U : 15U)
399
400 void *
401 slap_sl_calloc( ber_len_t n, ber_len_t size, void *ctx )
402 {
403         void *newptr;
404         ber_len_t total = n * size;
405
406         /* The sqrt test is a slight optimization: often avoids the division */
407         if ((n | size) <= LIM_SQRT(ber_len_t) || n == 0 || total/n == size) {
408                 newptr = slap_sl_malloc( total, ctx );
409                 memset( newptr, 0, n*size );
410         } else {
411                 Debug(LDAP_DEBUG_ANY, "slap_sl_calloc(%lu,%lu) out of range\n",
412                         (unsigned long) n, (unsigned long) size, 0);
413                 assert(0);
414                 exit(EXIT_FAILURE);
415         }
416         return newptr;
417 }
418
419 void *
420 slap_sl_realloc(void *ptr, ber_len_t size, void *ctx)
421 {
422         struct slab_heap *sh = ctx;
423         ber_len_t oldsize, *p = (ber_len_t *) ptr, *nextp;
424         void *newptr;
425
426         if (ptr == NULL)
427                 return slap_sl_malloc(size, ctx);
428
429         /* Not our memory? */
430         if (No_sl_malloc || !sh || ptr < sh->sh_base || ptr >= sh->sh_end) {
431                 /* Like ch_realloc(), except not trying a new context */
432                 newptr = ber_memrealloc_x(ptr, size, NULL);
433                 if (newptr) {
434                         return newptr;
435                 }
436                 Debug(LDAP_DEBUG_ANY, "slap_sl_realloc of %lu bytes failed\n",
437                         (unsigned long) size, 0, 0);
438                 assert(0);
439                 exit( EXIT_FAILURE );
440         }
441
442         if (size == 0) {
443                 slap_sl_free(ptr, ctx);
444                 return NULL;
445         }
446
447         oldsize = p[-1];
448
449         if (sh->sh_stack) {
450                 /* Add room for head, round up to doubleword boundary */
451                 size = (size + sizeof(ber_len_t) + Align-1) & -Align;
452
453                 p--;
454
455                 /* Never shrink blocks */
456                 if (size <= oldsize) {
457                         return ptr;
458                 }
459         
460                 oldsize &= -2;
461                 nextp = (ber_len_t *) ((char *) p + oldsize);
462
463                 /* If reallocing the last block, try to grow it */
464                 if (nextp == sh->sh_last) {
465                         if (size < (ber_len_t) ((char *) sh->sh_end - (char *) p)) {
466                                 sh->sh_last = (char *) p + size;
467                                 p[0] = (p[0] & 1) | size;
468                                 return ptr;
469                         }
470
471                 /* Nowhere to grow, need to alloc and copy */
472                 } else {
473                         /* Slight optimization of the final realloc variant */
474                         newptr = slap_sl_malloc(size-sizeof(ber_len_t), ctx);
475                         AC_MEMCPY(newptr, ptr, oldsize-sizeof(ber_len_t));
476                         /* Not last block, can just mark old region as free */
477                         nextp[-1] = oldsize;
478                         nextp[0] |= 1;
479                         return newptr;
480                 }
481
482                 size -= sizeof(ber_len_t);
483                 oldsize -= sizeof(ber_len_t);
484
485         } else if (oldsize > size) {
486                 oldsize = size;
487         }
488
489         newptr = slap_sl_malloc(size, ctx);
490         AC_MEMCPY(newptr, ptr, oldsize);
491         slap_sl_free(ptr, ctx);
492         return newptr;
493 }
494
495 void
496 slap_sl_free(void *ptr, void *ctx)
497 {
498         struct slab_heap *sh = ctx;
499         ber_len_t size;
500         ber_len_t *p = ptr, *nextp, *tmpp;
501
502         if (!ptr)
503                 return;
504
505         if (No_sl_malloc || !sh || ptr < sh->sh_base || ptr >= sh->sh_end) {
506                 ber_memfree_x(ptr, NULL);
507                 return;
508         }
509
510         size = *(--p);
511
512         if (sh->sh_stack) {
513                 size &= -2;
514                 nextp = (ber_len_t *) ((char *) p + size);
515                 if (sh->sh_last != nextp) {
516                         /* Mark it free: tail = size, head of next block |= 1 */
517                         nextp[-1] = size;
518                         nextp[0] |= 1;
519                         /* We can't tell Valgrind about it yet, because we
520                          * still need read/write access to this block for
521                          * when we eventually get to reclaim it.
522                          */
523                 } else {
524                         /* Reclaim freed block(s) off tail */
525                         while (*p & 1) {
526                                 p = (ber_len_t *) ((char *) p - p[-1]);
527                         }
528                         sh->sh_last = p;
529                         VGMEMP_TRIM(sh, sh->sh_base,
530                                 (char *) sh->sh_last - (char *) sh->sh_base);
531                 }
532
533         } else {
534                 int size_shift, order_size;
535                 struct slab_object *so;
536                 unsigned long diff;
537                 int i, inserted = 0, order = -1;
538
539                 size_shift = size + sizeof(ber_len_t) - 1;
540                 do {
541                         order++;
542                 } while (size_shift >>= 1);
543
544                 for (i = order, tmpp = p; i <= sh->sh_maxorder; i++) {
545                         order_size = 1 << (i+1);
546                         diff = (unsigned long)((char*)tmpp - (char*)sh->sh_base) >> (i+1);
547                         sh->sh_map[i-order_start][diff>>3] &= (~(1 << (diff & 0x7)));
548                         if (diff == ((diff>>1)<<1)) {
549                                 if (!(sh->sh_map[i-order_start][(diff+1)>>3] &
550                                                 (1<<((diff+1)&0x7)))) {
551                                         so = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
552                                         while (so) {
553                                                 if ((char*)so->so_ptr == (char*)tmpp) {
554                                                         LDAP_LIST_REMOVE( so, so_link );
555                                                 } else if ((char*)so->so_ptr ==
556                                                                 (char*)tmpp + order_size) {
557                                                         LDAP_LIST_REMOVE(so, so_link);
558                                                         break;
559                                                 }
560                                                 so = LDAP_LIST_NEXT(so, so_link);
561                                         }
562                                         if (so) {
563                                                 if (i < sh->sh_maxorder) {
564                                                         inserted = 1;
565                                                         so->so_ptr = tmpp;
566                                                         LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start+1],
567                                                                         so, so_link);
568                                                 }
569                                                 continue;
570                                         } else {
571                                                 if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
572                                                         slap_replenish_sopool(sh);
573                                                 }
574                                                 so = LDAP_LIST_FIRST(&sh->sh_sopool);
575                                                 LDAP_LIST_REMOVE(so, so_link);
576                                                 so->so_ptr = tmpp;
577                                                 LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
578                                                                 so, so_link);
579                                                 break;
580
581                                                 Debug(LDAP_DEBUG_TRACE, "slap_sl_free: "
582                                                         "free object not found while bit is clear.\n",
583                                                         0, 0, 0);
584                                                 assert(so != NULL);
585
586                                         }
587                                 } else {
588                                         if (!inserted) {
589                                                 if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
590                                                         slap_replenish_sopool(sh);
591                                                 }
592                                                 so = LDAP_LIST_FIRST(&sh->sh_sopool);
593                                                 LDAP_LIST_REMOVE(so, so_link);
594                                                 so->so_ptr = tmpp;
595                                                 LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
596                                                                 so, so_link);
597                                         }
598                                         break;
599                                 }
600                         } else {
601                                 if (!(sh->sh_map[i-order_start][(diff-1)>>3] &
602                                                 (1<<((diff-1)&0x7)))) {
603                                         so = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
604                                         while (so) {
605                                                 if ((char*)so->so_ptr == (char*)tmpp) {
606                                                         LDAP_LIST_REMOVE(so, so_link);
607                                                 } else if ((char*)tmpp == (char *)so->so_ptr + order_size) {
608                                                         LDAP_LIST_REMOVE(so, so_link);
609                                                         tmpp = so->so_ptr;
610                                                         break;
611                                                 }
612                                                 so = LDAP_LIST_NEXT(so, so_link);
613                                         }
614                                         if (so) {
615                                                 if (i < sh->sh_maxorder) {
616                                                         inserted = 1;
617                                                         LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start+1],                                                                    so, so_link);
618                                                         continue;
619                                                 }
620                                         } else {
621                                                 if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
622                                                         slap_replenish_sopool(sh);
623                                                 }
624                                                 so = LDAP_LIST_FIRST(&sh->sh_sopool);
625                                                 LDAP_LIST_REMOVE(so, so_link);
626                                                 so->so_ptr = tmpp;
627                                                 LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
628                                                                 so, so_link);
629                                                 break;
630
631                                                 Debug(LDAP_DEBUG_TRACE, "slap_sl_free: "
632                                                         "free object not found while bit is clear.\n",
633                                                         0, 0, 0 );
634                                                 assert(so != NULL);
635
636                                         }
637                                 } else {
638                                         if ( !inserted ) {
639                                                 if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
640                                                         slap_replenish_sopool(sh);
641                                                 }
642                                                 so = LDAP_LIST_FIRST(&sh->sh_sopool);
643                                                 LDAP_LIST_REMOVE(so, so_link);
644                                                 so->so_ptr = tmpp;
645                                                 LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
646                                                                 so, so_link);
647                                         }
648                                         break;
649                                 }
650                         }
651                 }
652         }
653 }
654
655 void
656 slap_sl_release( void *ptr, void *ctx )
657 {
658         struct slab_heap *sh = ctx;
659         if ( sh && ptr >= sh->sh_base && ptr <= sh->sh_end )
660                 sh->sh_last = ptr;
661 }
662
663 void *
664 slap_sl_mark( void *ctx )
665 {
666         struct slab_heap *sh = ctx;
667         return sh->sh_last;
668 }
669
670 /*
671  * Return the memory context of the current thread if the given block of
672  * memory belongs to it, otherwise return NULL.
673  */
674 void *
675 slap_sl_context( void *ptr )
676 {
677         void *memctx;
678         struct slab_heap *sh;
679
680         if ( slapMode & SLAP_TOOL_MODE ) return NULL;
681
682         sh = GET_MEMCTX(ldap_pvt_thread_pool_context(), &memctx);
683         if (sh && ptr >= sh->sh_base && ptr <= sh->sh_end) {
684                 return sh;
685         }
686         return NULL;
687 }
688
689 static struct slab_object *
690 slap_replenish_sopool(
691     struct slab_heap* sh
692 )
693 {
694     struct slab_object *so_block;
695     int i;
696
697     so_block = (struct slab_object *)ch_malloc(
698                     SLAP_SLAB_SOBLOCK * sizeof(struct slab_object));
699
700     if ( so_block == NULL ) {
701         return NULL;
702     }
703
704     so_block[0].so_blockhead = 1;
705     LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, &so_block[0], so_link);
706     for (i = 1; i < SLAP_SLAB_SOBLOCK; i++) {
707         so_block[i].so_blockhead = 0;
708         LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, &so_block[i], so_link );
709     }
710
711     return so_block;
712 }
713
714 #ifdef SLAPD_UNUSED
715 static void
716 print_slheap(int level, void *ctx)
717 {
718         struct slab_heap *sh = ctx;
719         struct slab_object *so;
720         int i, j, once = 0;
721
722         if (!ctx) {
723                 Debug(level, "NULL memctx\n", 0, 0, 0);
724                 return;
725         }
726
727         Debug(level, "sh->sh_maxorder=%d\n", sh->sh_maxorder, 0, 0);
728
729         for (i = order_start; i <= sh->sh_maxorder; i++) {
730                 once = 0;
731                 Debug(level, "order=%d\n", i, 0, 0);
732                 for (j = 0; j < (1<<(sh->sh_maxorder-i))/8; j++) {
733                         Debug(level, "%02x ", sh->sh_map[i-order_start][j], 0, 0);
734                         once = 1;
735                 }
736                 if (!once) {
737                         Debug(level, "%02x ", sh->sh_map[i-order_start][0], 0, 0);
738                 }
739                 Debug(level, "\n", 0, 0, 0);
740                 Debug(level, "free list:\n", 0, 0, 0);
741                 so = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
742                 while (so) {
743                         Debug(level, "%p\n", so->so_ptr, 0, 0);
744                         so = LDAP_LIST_NEXT(so, so_link);
745                 }
746         }
747 }
748 #endif