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