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