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