mem.c revision 135446
1/* 2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1997-2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id: mem.c,v 1.98.2.7.2.5 2004/03/16 05:50:24 marka Exp $ */ 19 20#include <config.h> 21 22#include <stdio.h> 23#include <stdlib.h> 24#include <stddef.h> 25 26#include <limits.h> 27 28#include <isc/magic.h> 29#include <isc/mem.h> 30#include <isc/msgs.h> 31#include <isc/ondestroy.h> 32#include <isc/string.h> 33 34#include <isc/mutex.h> 35#include <isc/util.h> 36 37#ifndef ISC_MEM_DEBUGGING 38#define ISC_MEM_DEBUGGING 0 39#endif 40LIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING; 41 42/* 43 * Define ISC_MEM_USE_INTERNAL_MALLOC=1 to use the internal malloc() 44 * implementation in preference to the system one. The internal malloc() 45 * is very space-efficient, and quite fast on uniprocessor systems. It 46 * performs poorly on multiprocessor machines. 47 */ 48#ifndef ISC_MEM_USE_INTERNAL_MALLOC 49#define ISC_MEM_USE_INTERNAL_MALLOC 0 50#endif 51 52/* 53 * Constants. 54 */ 55 56#define DEF_MAX_SIZE 1100 57#define DEF_MEM_TARGET 4096 58#define ALIGNMENT_SIZE 8 /* must be a power of 2 */ 59#define NUM_BASIC_BLOCKS 64 /* must be > 1 */ 60#define TABLE_INCREMENT 1024 61#define DEBUGLIST_COUNT 1024 62 63/* 64 * Types. 65 */ 66#if ISC_MEM_TRACKLINES 67typedef struct debuglink debuglink_t; 68struct debuglink { 69 ISC_LINK(debuglink_t) link; 70 const void *ptr[DEBUGLIST_COUNT]; 71 unsigned int size[DEBUGLIST_COUNT]; 72 const char *file[DEBUGLIST_COUNT]; 73 unsigned int line[DEBUGLIST_COUNT]; 74 unsigned int count; 75}; 76 77#define FLARG_PASS , file, line 78#define FLARG , const char *file, int line 79#else 80#define FLARG_PASS 81#define FLARG 82#endif 83 84typedef struct element element; 85struct element { 86 element * next; 87}; 88 89typedef struct { 90 /* 91 * This structure must be ALIGNMENT_SIZE bytes. 92 */ 93 union { 94 size_t size; 95 char bytes[ALIGNMENT_SIZE]; 96 } u; 97} size_info; 98 99struct stats { 100 unsigned long gets; 101 unsigned long totalgets; 102#if ISC_MEM_USE_INTERNAL_MALLOC 103 unsigned long blocks; 104 unsigned long freefrags; 105#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 106}; 107 108#define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C') 109#define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC) 110 111#if ISC_MEM_TRACKLINES 112typedef ISC_LIST(debuglink_t) debuglist_t; 113#endif 114 115struct isc_mem { 116 unsigned int magic; 117 isc_ondestroy_t ondestroy; 118 isc_mutex_t lock; 119 isc_memalloc_t memalloc; 120 isc_memfree_t memfree; 121 void * arg; 122 size_t max_size; 123 isc_boolean_t checkfree; 124 struct stats * stats; 125 unsigned int references; 126 size_t quota; 127 size_t total; 128 size_t inuse; 129 size_t maxinuse; 130 size_t hi_water; 131 size_t lo_water; 132 isc_boolean_t hi_called; 133 isc_mem_water_t water; 134 void * water_arg; 135 ISC_LIST(isc_mempool_t) pools; 136 137#if ISC_MEM_USE_INTERNAL_MALLOC 138 size_t mem_target; 139 element ** freelists; 140 element * basic_blocks; 141 unsigned char ** basic_table; 142 unsigned int basic_table_count; 143 unsigned int basic_table_size; 144 unsigned char * lowest; 145 unsigned char * highest; 146#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 147 148#if ISC_MEM_TRACKLINES 149 debuglist_t * debuglist; 150#endif 151 152 unsigned int memalloc_failures; 153}; 154 155#define MEMPOOL_MAGIC ISC_MAGIC('M', 'E', 'M', 'p') 156#define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC) 157 158struct isc_mempool { 159 /* always unlocked */ 160 unsigned int magic; /* magic number */ 161 isc_mutex_t *lock; /* optional lock */ 162 isc_mem_t *mctx; /* our memory context */ 163 /* locked via the memory context's lock */ 164 ISC_LINK(isc_mempool_t) link; /* next pool in this mem context */ 165 /* optionally locked from here down */ 166 element *items; /* low water item list */ 167 size_t size; /* size of each item on this pool */ 168 unsigned int maxalloc; /* max number of items allowed */ 169 unsigned int allocated; /* # of items currently given out */ 170 unsigned int freecount; /* # of items on reserved list */ 171 unsigned int freemax; /* # of items allowed on free list */ 172 unsigned int fillcount; /* # of items to fetch on each fill */ 173 /* Stats only. */ 174 unsigned int gets; /* # of requests to this pool */ 175 /* Debugging only. */ 176#if ISC_MEMPOOL_NAMES 177 char name[16]; /* printed name in stats reports */ 178#endif 179}; 180 181/* 182 * Private Inline-able. 183 */ 184 185#if ! ISC_MEM_TRACKLINES 186#define ADD_TRACE(a, b, c, d, e) 187#define DELETE_TRACE(a, b, c, d, e) 188#else 189#define ADD_TRACE(a, b, c, d, e) \ 190 do { \ 191 if ((isc_mem_debugging & (ISC_MEM_DEBUGTRACE | \ 192 ISC_MEM_DEBUGRECORD)) != 0 && \ 193 b != NULL) \ 194 add_trace_entry(a, b, c, d, e); \ 195 } while (0) 196#define DELETE_TRACE(a, b, c, d, e) delete_trace_entry(a, b, c, d, e) 197 198static void 199print_active(isc_mem_t *ctx, FILE *out); 200 201/* 202 * mctx must be locked. 203 */ 204static inline void 205add_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size 206 FLARG) 207{ 208 debuglink_t *dl; 209 unsigned int i; 210 211 if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) 212 fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 213 ISC_MSG_ADDTRACE, 214 "add %p size %u " 215 "file %s line %u mctx %p\n"), 216 ptr, size, file, line, mctx); 217 218 if (mctx->debuglist == NULL) 219 return; 220 221 if (size > mctx->max_size) 222 size = mctx->max_size; 223 224 dl = ISC_LIST_HEAD(mctx->debuglist[size]); 225 while (dl != NULL) { 226 if (dl->count == DEBUGLIST_COUNT) 227 goto next; 228 for (i = 0; i < DEBUGLIST_COUNT; i++) { 229 if (dl->ptr[i] == NULL) { 230 dl->ptr[i] = ptr; 231 dl->size[i] = size; 232 dl->file[i] = file; 233 dl->line[i] = line; 234 dl->count++; 235 return; 236 } 237 } 238 next: 239 dl = ISC_LIST_NEXT(dl, link); 240 } 241 242 dl = malloc(sizeof(debuglink_t)); 243 INSIST(dl != NULL); 244 245 ISC_LINK_INIT(dl, link); 246 for (i = 1; i < DEBUGLIST_COUNT; i++) { 247 dl->ptr[i] = NULL; 248 dl->size[i] = 0; 249 dl->file[i] = NULL; 250 dl->line[i] = 0; 251 } 252 253 dl->ptr[0] = ptr; 254 dl->size[0] = size; 255 dl->file[0] = file; 256 dl->line[0] = line; 257 dl->count = 1; 258 259 ISC_LIST_PREPEND(mctx->debuglist[size], dl, link); 260} 261 262static inline void 263delete_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size, 264 const char *file, unsigned int line) 265{ 266 debuglink_t *dl; 267 unsigned int i; 268 269 if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) 270 fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 271 ISC_MSG_DELTRACE, 272 "del %p size %u " 273 "file %s line %u mctx %p\n"), 274 ptr, size, file, line, mctx); 275 276 if (mctx->debuglist == NULL) 277 return; 278 279 if (size > mctx->max_size) 280 size = mctx->max_size; 281 282 dl = ISC_LIST_HEAD(mctx->debuglist[size]); 283 while (dl != NULL) { 284 for (i = 0; i < DEBUGLIST_COUNT; i++) { 285 if (dl->ptr[i] == ptr) { 286 dl->ptr[i] = NULL; 287 dl->size[i] = 0; 288 dl->file[i] = NULL; 289 dl->line[i] = 0; 290 291 INSIST(dl->count > 0); 292 dl->count--; 293 if (dl->count == 0) { 294 ISC_LIST_UNLINK(mctx->debuglist[size], 295 dl, link); 296 free(dl); 297 } 298 return; 299 } 300 } 301 dl = ISC_LIST_NEXT(dl, link); 302 } 303 304 /* 305 * If we get here, we didn't find the item on the list. We're 306 * screwed. 307 */ 308 INSIST(dl != NULL); 309} 310#endif /* ISC_MEM_TRACKLINES */ 311 312#if ISC_MEM_USE_INTERNAL_MALLOC 313static inline size_t 314rmsize(size_t size) { 315 /* 316 * round down to ALIGNMENT_SIZE 317 */ 318 return (size & (~(ALIGNMENT_SIZE - 1))); 319} 320 321static inline size_t 322quantize(size_t size) { 323 /* 324 * Round up the result in order to get a size big 325 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE 326 * byte boundaries. 327 */ 328 329 if (size == 0) 330 return (ALIGNMENT_SIZE); 331 return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1))); 332} 333 334static inline isc_boolean_t 335more_basic_blocks(isc_mem_t *ctx) { 336 void *new; 337 unsigned char *curr, *next; 338 unsigned char *first, *last; 339 unsigned char **table; 340 unsigned int table_size; 341 size_t increment; 342 int i; 343 344 /* Require: we hold the context lock. */ 345 346 /* 347 * Did we hit the quota for this context? 348 */ 349 increment = NUM_BASIC_BLOCKS * ctx->mem_target; 350 if (ctx->quota != 0 && ctx->total + increment > ctx->quota) 351 return (ISC_FALSE); 352 353 INSIST(ctx->basic_table_count <= ctx->basic_table_size); 354 if (ctx->basic_table_count == ctx->basic_table_size) { 355 table_size = ctx->basic_table_size + TABLE_INCREMENT; 356 table = (ctx->memalloc)(ctx->arg, 357 table_size * sizeof(unsigned char *)); 358 if (table == NULL) { 359 ctx->memalloc_failures++; 360 return (ISC_FALSE); 361 } 362 if (ctx->basic_table_size != 0) { 363 memcpy(table, ctx->basic_table, 364 ctx->basic_table_size * 365 sizeof(unsigned char *)); 366 (ctx->memfree)(ctx->arg, ctx->basic_table); 367 } 368 ctx->basic_table = table; 369 ctx->basic_table_size = table_size; 370 } 371 372 new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target); 373 if (new == NULL) { 374 ctx->memalloc_failures++; 375 return (ISC_FALSE); 376 } 377 ctx->total += increment; 378 ctx->basic_table[ctx->basic_table_count] = new; 379 ctx->basic_table_count++; 380 381 curr = new; 382 next = curr + ctx->mem_target; 383 for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) { 384 ((element *)curr)->next = (element *)next; 385 curr = next; 386 next += ctx->mem_target; 387 } 388 /* 389 * curr is now pointing at the last block in the 390 * array. 391 */ 392 ((element *)curr)->next = NULL; 393 first = new; 394 last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1; 395 if (first < ctx->lowest || ctx->lowest == NULL) 396 ctx->lowest = first; 397 if (last > ctx->highest) 398 ctx->highest = last; 399 ctx->basic_blocks = new; 400 401 return (ISC_TRUE); 402} 403 404static inline isc_boolean_t 405more_frags(isc_mem_t *ctx, size_t new_size) { 406 int i, frags; 407 size_t total_size; 408 void *new; 409 unsigned char *curr, *next; 410 411 /* 412 * Try to get more fragments by chopping up a basic block. 413 */ 414 415 if (ctx->basic_blocks == NULL) { 416 if (!more_basic_blocks(ctx)) { 417 /* 418 * We can't get more memory from the OS, or we've 419 * hit the quota for this context. 420 */ 421 /* 422 * XXXRTH "At quota" notification here. 423 */ 424 return (ISC_FALSE); 425 } 426 } 427 428 total_size = ctx->mem_target; 429 new = ctx->basic_blocks; 430 ctx->basic_blocks = ctx->basic_blocks->next; 431 frags = total_size / new_size; 432 ctx->stats[new_size].blocks++; 433 ctx->stats[new_size].freefrags += frags; 434 /* 435 * Set up a linked-list of blocks of size 436 * "new_size". 437 */ 438 curr = new; 439 next = curr + new_size; 440 total_size -= new_size; 441 for (i = 0; i < (frags - 1); i++) { 442 ((element *)curr)->next = (element *)next; 443 curr = next; 444 next += new_size; 445 total_size -= new_size; 446 } 447 /* 448 * Add the remaining fragment of the basic block to a free list. 449 */ 450 total_size = rmsize(total_size); 451 if (total_size > 0) { 452 ((element *)next)->next = ctx->freelists[total_size]; 453 ctx->freelists[total_size] = (element *)next; 454 ctx->stats[total_size].freefrags++; 455 } 456 /* 457 * curr is now pointing at the last block in the 458 * array. 459 */ 460 ((element *)curr)->next = NULL; 461 ctx->freelists[new_size] = new; 462 463 return (ISC_TRUE); 464} 465 466static inline void * 467mem_getunlocked(isc_mem_t *ctx, size_t size) { 468 size_t new_size = quantize(size); 469 void *ret; 470 471 if (size >= ctx->max_size || new_size >= ctx->max_size) { 472 /* 473 * memget() was called on something beyond our upper limit. 474 */ 475 if (ctx->quota != 0 && ctx->total + size > ctx->quota) { 476 ret = NULL; 477 goto done; 478 } 479 ret = (ctx->memalloc)(ctx->arg, size); 480 if (ret == NULL) { 481 ctx->memalloc_failures++; 482 goto done; 483 } 484 ctx->total += size; 485 ctx->inuse += size; 486 ctx->stats[ctx->max_size].gets++; 487 ctx->stats[ctx->max_size].totalgets++; 488 /* 489 * If we don't set new_size to size, then the 490 * ISC_MEM_FILL code might write over bytes we 491 * don't own. 492 */ 493 new_size = size; 494 goto done; 495 } 496 497 /* 498 * If there are no blocks in the free list for this size, get a chunk 499 * of memory and then break it up into "new_size"-sized blocks, adding 500 * them to the free list. 501 */ 502 if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size)) 503 return (NULL); 504 505 /* 506 * The free list uses the "rounded-up" size "new_size". 507 */ 508 ret = ctx->freelists[new_size]; 509 ctx->freelists[new_size] = ctx->freelists[new_size]->next; 510 511 /* 512 * The stats[] uses the _actual_ "size" requested by the 513 * caller, with the caveat (in the code above) that "size" >= the 514 * max. size (max_size) ends up getting recorded as a call to 515 * max_size. 516 */ 517 ctx->stats[size].gets++; 518 ctx->stats[size].totalgets++; 519 ctx->stats[new_size].freefrags--; 520 ctx->inuse += new_size; 521 522 done: 523 524#if ISC_MEM_FILL 525 if (ret != NULL) 526 memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */ 527#endif 528 529 return (ret); 530} 531 532#if ISC_MEM_FILL && ISC_MEM_CHECKOVERRUN 533static inline void 534check_overrun(void *mem, size_t size, size_t new_size) { 535 unsigned char *cp; 536 537 cp = (unsigned char *)mem; 538 cp += size; 539 while (size < new_size) { 540 INSIST(*cp == 0xbe); 541 cp++; 542 size++; 543 } 544} 545#endif 546 547static inline void 548mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size) { 549 size_t new_size = quantize(size); 550 551 if (size == ctx->max_size || new_size >= ctx->max_size) { 552 /* 553 * memput() called on something beyond our upper limit. 554 */ 555#if ISC_MEM_FILL 556 memset(mem, 0xde, size); /* Mnemonic for "dead". */ 557#endif 558 (ctx->memfree)(ctx->arg, mem); 559 INSIST(ctx->stats[ctx->max_size].gets != 0); 560 ctx->stats[ctx->max_size].gets--; 561 INSIST(size <= ctx->total); 562 ctx->inuse -= size; 563 ctx->total -= size; 564 return; 565 } 566 567#if ISC_MEM_FILL 568#if ISC_MEM_CHECKOVERRUN 569 check_overrun(mem, size, new_size); 570#endif 571 memset(mem, 0xde, new_size); /* Mnemonic for "dead". */ 572#endif 573 574 /* 575 * The free list uses the "rounded-up" size "new_size". 576 */ 577 ((element *)mem)->next = ctx->freelists[new_size]; 578 ctx->freelists[new_size] = (element *)mem; 579 580 /* 581 * The stats[] uses the _actual_ "size" requested by the 582 * caller, with the caveat (in the code above) that "size" >= the 583 * max. size (max_size) ends up getting recorded as a call to 584 * max_size. 585 */ 586 INSIST(ctx->stats[size].gets != 0); 587 ctx->stats[size].gets--; 588 ctx->stats[new_size].freefrags++; 589 ctx->inuse -= new_size; 590} 591 592#else /* ISC_MEM_USE_INTERNAL_MALLOC */ 593 594/* 595 * Perform a malloc, doing memory filling and overrun detection as necessary. 596 */ 597static inline void * 598mem_get(isc_mem_t *ctx, size_t size) { 599 char *ret; 600 601#if ISC_MEM_CHECKOVERRUN 602 size += 1; 603#endif 604 605 ret = (ctx->memalloc)(ctx->arg, size); 606 if (ret == NULL) 607 ctx->memalloc_failures++; 608 609#if ISC_MEM_FILL 610 if (ret != NULL) 611 memset(ret, 0xbe, size); /* Mnemonic for "beef". */ 612#else 613# if ISC_MEM_CHECKOVERRUN 614 if (ret != NULL) 615 ret[size-1] = 0xbe; 616# endif 617#endif 618 619 return (ret); 620} 621 622/* 623 * Perform a free, doing memory filling and overrun detection as necessary. 624 */ 625static inline void 626mem_put(isc_mem_t *ctx, void *mem, size_t size) { 627#if ISC_MEM_CHECKOVERRUN 628 INSIST(((unsigned char *)mem)[size] == 0xbe); 629#endif 630#if ISC_MEM_FILL 631 memset(mem, 0xde, size); /* Mnemonic for "dead". */ 632#else 633 UNUSED(size); 634#endif 635 (ctx->memfree)(ctx->arg, mem); 636} 637 638/* 639 * Update internal counters after a memory get. 640 */ 641static inline void 642mem_getstats(isc_mem_t *ctx, size_t size) { 643 ctx->total += size; 644 ctx->inuse += size; 645 646 if (size > ctx->max_size) { 647 ctx->stats[ctx->max_size].gets++; 648 ctx->stats[ctx->max_size].totalgets++; 649 } else { 650 ctx->stats[size].gets++; 651 ctx->stats[size].totalgets++; 652 } 653} 654 655/* 656 * Update internal counters after a memory put. 657 */ 658static inline void 659mem_putstats(isc_mem_t *ctx, void *ptr, size_t size) { 660 UNUSED(ptr); 661 662 INSIST(ctx->inuse >= size); 663 ctx->inuse -= size; 664 665 if (size > ctx->max_size) { 666 INSIST(ctx->stats[ctx->max_size].gets > 0U); 667 ctx->stats[ctx->max_size].gets--; 668 } else { 669 INSIST(ctx->stats[size].gets > 0U); 670 ctx->stats[size].gets--; 671 } 672} 673 674#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 675 676/* 677 * Private. 678 */ 679 680static void * 681default_memalloc(void *arg, size_t size) { 682 UNUSED(arg); 683 if (size == 0U) 684 size = 1; 685 return (malloc(size)); 686} 687 688static void 689default_memfree(void *arg, void *ptr) { 690 UNUSED(arg); 691 free(ptr); 692} 693 694/* 695 * Public. 696 */ 697 698isc_result_t 699isc_mem_createx(size_t init_max_size, size_t target_size, 700 isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg, 701 isc_mem_t **ctxp) 702{ 703 isc_mem_t *ctx; 704 isc_result_t result; 705 706 REQUIRE(ctxp != NULL && *ctxp == NULL); 707 REQUIRE(memalloc != NULL); 708 REQUIRE(memfree != NULL); 709 710 INSIST((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0); 711 712#if !ISC_MEM_USE_INTERNAL_MALLOC 713 UNUSED(target_size); 714#endif 715 716 ctx = (memalloc)(arg, sizeof(*ctx)); 717 if (ctx == NULL) 718 return (ISC_R_NOMEMORY); 719 720 if (init_max_size == 0U) 721 ctx->max_size = DEF_MAX_SIZE; 722 else 723 ctx->max_size = init_max_size; 724 ctx->references = 1; 725 ctx->quota = 0; 726 ctx->total = 0; 727 ctx->inuse = 0; 728 ctx->maxinuse = 0; 729 ctx->hi_water = 0; 730 ctx->lo_water = 0; 731 ctx->hi_called = ISC_FALSE; 732 ctx->water = NULL; 733 ctx->water_arg = NULL; 734 ctx->magic = MEM_MAGIC; 735 isc_ondestroy_init(&ctx->ondestroy); 736 ctx->memalloc = memalloc; 737 ctx->memfree = memfree; 738 ctx->arg = arg; 739 ctx->stats = NULL; 740 ctx->checkfree = ISC_TRUE; 741#if ISC_MEM_TRACKLINES 742 ctx->debuglist = NULL; 743#endif 744 ISC_LIST_INIT(ctx->pools); 745 746#if ISC_MEM_USE_INTERNAL_MALLOC 747 ctx->freelists = NULL; 748#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 749 750 ctx->stats = (memalloc)(arg, 751 (ctx->max_size+1) * sizeof(struct stats)); 752 if (ctx->stats == NULL) { 753 result = ISC_R_NOMEMORY; 754 goto error; 755 } 756 memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats)); 757 758#if ISC_MEM_USE_INTERNAL_MALLOC 759 if (target_size == 0) 760 ctx->mem_target = DEF_MEM_TARGET; 761 else 762 ctx->mem_target = target_size; 763 ctx->freelists = (memalloc)(arg, ctx->max_size * sizeof(element *)); 764 if (ctx->freelists == NULL) { 765 result = ISC_R_NOMEMORY; 766 goto error; 767 } 768 memset(ctx->freelists, 0, 769 ctx->max_size * sizeof(element *)); 770 ctx->basic_blocks = NULL; 771 ctx->basic_table = NULL; 772 ctx->basic_table_count = 0; 773 ctx->basic_table_size = 0; 774 ctx->lowest = NULL; 775 ctx->highest = NULL; 776#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 777 778 if (isc_mutex_init(&ctx->lock) != ISC_R_SUCCESS) { 779 UNEXPECTED_ERROR(__FILE__, __LINE__, 780 "isc_mutex_init() %s", 781 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 782 ISC_MSG_FAILED, "failed")); 783 result = ISC_R_UNEXPECTED; 784 goto error; 785 } 786 787#if ISC_MEM_TRACKLINES 788 if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) { 789 unsigned int i; 790 791 ctx->debuglist = (memalloc)(arg, 792 (ctx->max_size+1) * sizeof(debuglist_t)); 793 if (ctx->debuglist == NULL) { 794 result = ISC_R_NOMEMORY; 795 goto error; 796 } 797 for (i = 0; i <= ctx->max_size; i++) 798 ISC_LIST_INIT(ctx->debuglist[i]); 799 } 800#endif 801 802 ctx->memalloc_failures = 0; 803 804 *ctxp = ctx; 805 return (ISC_R_SUCCESS); 806 807 error: 808 if (ctx) { 809 if (ctx->stats) 810 (memfree)(arg, ctx->stats); 811#if ISC_MEM_USE_INTERNAL_MALLOC 812 if (ctx->freelists) 813 (memfree)(arg, ctx->freelists); 814#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 815#if ISC_MEM_TRACKLINES 816 if (ctx->debuglist) 817 (ctx->memfree)(ctx->arg, ctx->debuglist); 818#endif /* ISC_MEM_TRACKLINES */ 819 (memfree)(arg, ctx); 820 } 821 822 return (result); 823} 824 825isc_result_t 826isc_mem_create(size_t init_max_size, size_t target_size, 827 isc_mem_t **ctxp) 828{ 829 return (isc_mem_createx(init_max_size, target_size, 830 default_memalloc, default_memfree, NULL, 831 ctxp)); 832} 833 834static void 835destroy(isc_mem_t *ctx) { 836 unsigned int i; 837 isc_ondestroy_t ondest; 838 839 ctx->magic = 0; 840 841#if ISC_MEM_USE_INTERNAL_MALLOC 842 INSIST(ISC_LIST_EMPTY(ctx->pools)); 843#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 844 845#if ISC_MEM_TRACKLINES 846 if (ctx->debuglist != NULL) { 847 if (ctx->checkfree) { 848 for (i = 0; i <= ctx->max_size; i++) { 849 if (!ISC_LIST_EMPTY(ctx->debuglist[i])) 850 print_active(ctx, stderr); 851 INSIST(ISC_LIST_EMPTY(ctx->debuglist[i])); 852 } 853 } else { 854 debuglink_t *dl; 855 856 for (i = 0; i <= ctx->max_size; i++) 857 for (dl = ISC_LIST_HEAD(ctx->debuglist[i]); 858 dl != NULL; 859 dl = ISC_LIST_HEAD(ctx->debuglist[i])) { 860 ISC_LIST_UNLINK(ctx->debuglist[i], 861 dl, link); 862 free(dl); 863 } 864 } 865 (ctx->memfree)(ctx->arg, ctx->debuglist); 866 } 867#endif 868 INSIST(ctx->references == 0); 869 870 if (ctx->checkfree) { 871 for (i = 0; i <= ctx->max_size; i++) { 872#if ISC_MEM_TRACKLINES 873 if (ctx->stats[i].gets != 0U) 874 print_active(ctx, stderr); 875#endif 876 INSIST(ctx->stats[i].gets == 0U); 877 } 878 } 879 880 (ctx->memfree)(ctx->arg, ctx->stats); 881 882#if ISC_MEM_USE_INTERNAL_MALLOC 883 for (i = 0; i < ctx->basic_table_count; i++) 884 (ctx->memfree)(ctx->arg, ctx->basic_table[i]); 885 (ctx->memfree)(ctx->arg, ctx->freelists); 886 (ctx->memfree)(ctx->arg, ctx->basic_table); 887#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 888 889 ondest = ctx->ondestroy; 890 891 DESTROYLOCK(&ctx->lock); 892 (ctx->memfree)(ctx->arg, ctx); 893 894 isc_ondestroy_notify(&ondest, ctx); 895} 896 897void 898isc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) { 899 REQUIRE(VALID_CONTEXT(source)); 900 REQUIRE(targetp != NULL && *targetp == NULL); 901 902 LOCK(&source->lock); 903 source->references++; 904 UNLOCK(&source->lock); 905 906 *targetp = source; 907} 908 909void 910isc_mem_detach(isc_mem_t **ctxp) { 911 isc_mem_t *ctx; 912 isc_boolean_t want_destroy = ISC_FALSE; 913 914 REQUIRE(ctxp != NULL); 915 ctx = *ctxp; 916 REQUIRE(VALID_CONTEXT(ctx)); 917 918 LOCK(&ctx->lock); 919 INSIST(ctx->references > 0); 920 ctx->references--; 921 if (ctx->references == 0) 922 want_destroy = ISC_TRUE; 923 UNLOCK(&ctx->lock); 924 925 if (want_destroy) 926 destroy(ctx); 927 928 *ctxp = NULL; 929} 930 931/* 932 * isc_mem_putanddetach() is the equivalent of: 933 * 934 * mctx = NULL; 935 * isc_mem_attach(ptr->mctx, &mctx); 936 * isc_mem_detach(&ptr->mctx); 937 * isc_mem_put(mctx, ptr, sizeof(*ptr); 938 * isc_mem_detach(&mctx); 939 */ 940 941void 942isc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) { 943 isc_mem_t *ctx; 944 isc_boolean_t want_destroy = ISC_FALSE; 945 946 REQUIRE(ctxp != NULL); 947 ctx = *ctxp; 948 REQUIRE(VALID_CONTEXT(ctx)); 949 REQUIRE(ptr != NULL); 950 951 /* 952 * Must be before mem_putunlocked() as ctxp is usually within 953 * [ptr..ptr+size). 954 */ 955 *ctxp = NULL; 956 957#if ISC_MEM_USE_INTERNAL_MALLOC 958 LOCK(&ctx->lock); 959 mem_putunlocked(ctx, ptr, size); 960#else /* ISC_MEM_USE_INTERNAL_MALLOC */ 961 mem_put(ctx, ptr, size); 962 LOCK(&ctx->lock); 963 mem_putstats(ctx, ptr, size); 964#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 965 966 DELETE_TRACE(ctx, ptr, size, file, line); 967 INSIST(ctx->references > 0); 968 ctx->references--; 969 if (ctx->references == 0) 970 want_destroy = ISC_TRUE; 971 972 UNLOCK(&ctx->lock); 973 974 if (want_destroy) 975 destroy(ctx); 976} 977 978void 979isc_mem_destroy(isc_mem_t **ctxp) { 980 isc_mem_t *ctx; 981 982 /* 983 * This routine provides legacy support for callers who use mctxs 984 * without attaching/detaching. 985 */ 986 987 REQUIRE(ctxp != NULL); 988 ctx = *ctxp; 989 REQUIRE(VALID_CONTEXT(ctx)); 990 991 LOCK(&ctx->lock); 992#if ISC_MEM_TRACKLINES 993 if (ctx->references != 1) 994 print_active(ctx, stderr); 995#endif 996 REQUIRE(ctx->references == 1); 997 ctx->references--; 998 UNLOCK(&ctx->lock); 999 1000 destroy(ctx); 1001 1002 *ctxp = NULL; 1003} 1004 1005isc_result_t 1006isc_mem_ondestroy(isc_mem_t *ctx, isc_task_t *task, isc_event_t **event) { 1007 isc_result_t res; 1008 1009 LOCK(&ctx->lock); 1010 res = isc_ondestroy_register(&ctx->ondestroy, task, event); 1011 UNLOCK(&ctx->lock); 1012 1013 return (res); 1014} 1015 1016 1017void * 1018isc__mem_get(isc_mem_t *ctx, size_t size FLARG) { 1019 void *ptr; 1020 isc_boolean_t call_water = ISC_FALSE; 1021 1022 REQUIRE(VALID_CONTEXT(ctx)); 1023 1024#if ISC_MEM_USE_INTERNAL_MALLOC 1025 LOCK(&ctx->lock); 1026 ptr = mem_getunlocked(ctx, size); 1027#else /* ISC_MEM_USE_INTERNAL_MALLOC */ 1028 ptr = mem_get(ctx, size); 1029 LOCK(&ctx->lock); 1030 if (ptr != NULL) 1031 mem_getstats(ctx, size); 1032#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 1033 1034 ADD_TRACE(ctx, ptr, size, file, line); 1035 if (ctx->hi_water != 0U && !ctx->hi_called && 1036 ctx->inuse > ctx->hi_water) { 1037 ctx->hi_called = ISC_TRUE; 1038 call_water = ISC_TRUE; 1039 } 1040 if (ctx->inuse > ctx->maxinuse) { 1041 ctx->maxinuse = ctx->inuse; 1042 if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water && 1043 (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) 1044 fprintf(stderr, "maxinuse = %lu\n", 1045 (unsigned long)ctx->inuse); 1046 } 1047 UNLOCK(&ctx->lock); 1048 1049 if (call_water) 1050 (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER); 1051 1052 return (ptr); 1053} 1054 1055void 1056isc__mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG) 1057{ 1058 isc_boolean_t call_water = ISC_FALSE; 1059 1060 REQUIRE(VALID_CONTEXT(ctx)); 1061 REQUIRE(ptr != NULL); 1062 1063#if ISC_MEM_USE_INTERNAL_MALLOC 1064 LOCK(&ctx->lock); 1065 mem_putunlocked(ctx, ptr, size); 1066#else /* ISC_MEM_USE_INTERNAL_MALLOC */ 1067 mem_put(ctx, ptr, size); 1068 LOCK(&ctx->lock); 1069 mem_putstats(ctx, ptr, size); 1070#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 1071 1072 DELETE_TRACE(ctx, ptr, size, file, line); 1073 1074 /* 1075 * The check against ctx->lo_water == 0 is for the condition 1076 * when the context was pushed over hi_water but then had 1077 * isc_mem_setwater() called with 0 for hi_water and lo_water. 1078 */ 1079 if (ctx->hi_called && 1080 (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) { 1081 ctx->hi_called = ISC_FALSE; 1082 1083 if (ctx->water != NULL) 1084 call_water = ISC_TRUE; 1085 } 1086 UNLOCK(&ctx->lock); 1087 1088 if (call_water) 1089 (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER); 1090} 1091 1092#if ISC_MEM_TRACKLINES 1093static void 1094print_active(isc_mem_t *mctx, FILE *out) { 1095 if (mctx->debuglist != NULL) { 1096 debuglink_t *dl; 1097 unsigned int i, j; 1098 const char *format; 1099 isc_boolean_t found; 1100 1101 fprintf(out, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1102 ISC_MSG_DUMPALLOC, 1103 "Dump of all outstanding " 1104 "memory allocations:\n")); 1105 found = ISC_FALSE; 1106 format = isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1107 ISC_MSG_PTRFILELINE, 1108 "\tptr %p size %u file %s line %u\n"); 1109 for (i = 0; i <= mctx->max_size; i++) { 1110 dl = ISC_LIST_HEAD(mctx->debuglist[i]); 1111 1112 if (dl != NULL) 1113 found = ISC_TRUE; 1114 1115 while (dl != NULL) { 1116 for (j = 0; j < DEBUGLIST_COUNT; j++) 1117 if (dl->ptr[j] != NULL) 1118 fprintf(out, format, 1119 dl->ptr[j], 1120 dl->size[j], 1121 dl->file[j], 1122 dl->line[j]); 1123 dl = ISC_LIST_NEXT(dl, link); 1124 } 1125 } 1126 if (!found) 1127 fprintf(out, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1128 ISC_MSG_NONE, "\tNone.\n")); 1129 } 1130} 1131#endif 1132 1133/* 1134 * Print the stats[] on the stream "out" with suitable formatting. 1135 */ 1136void 1137isc_mem_stats(isc_mem_t *ctx, FILE *out) { 1138 size_t i; 1139 const struct stats *s; 1140 const isc_mempool_t *pool; 1141 1142 REQUIRE(VALID_CONTEXT(ctx)); 1143 LOCK(&ctx->lock); 1144 1145 for (i = 0; i <= ctx->max_size; i++) { 1146 s = &ctx->stats[i]; 1147 1148 if (s->totalgets == 0U && s->gets == 0U) 1149 continue; 1150 fprintf(out, "%s%5lu: %11lu gets, %11lu rem", 1151 (i == ctx->max_size) ? ">=" : " ", 1152 (unsigned long) i, s->totalgets, s->gets); 1153#if ISC_MEM_USE_INTERNAL_MALLOC 1154 if (s->blocks != 0 || s->freefrags != 0) 1155 fprintf(out, " (%lu bl, %lu ff)", 1156 s->blocks, s->freefrags); 1157#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 1158 fputc('\n', out); 1159 } 1160 1161 /* 1162 * Note that since a pool can be locked now, these stats might be 1163 * somewhat off if the pool is in active use at the time the stats 1164 * are dumped. The link fields are protected by the isc_mem_t's 1165 * lock, however, so walking this list and extracting integers from 1166 * stats fields is always safe. 1167 */ 1168 pool = ISC_LIST_HEAD(ctx->pools); 1169 if (pool != NULL) { 1170 fprintf(out, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1171 ISC_MSG_POOLSTATS, 1172 "[Pool statistics]\n")); 1173 fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %10s %1s\n", 1174 isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1175 ISC_MSG_POOLNAME, "name"), 1176 isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1177 ISC_MSG_POOLSIZE, "size"), 1178 isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1179 ISC_MSG_POOLMAXALLOC, "maxalloc"), 1180 isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1181 ISC_MSG_POOLALLOCATED, "allocated"), 1182 isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1183 ISC_MSG_POOLFREECOUNT, "freecount"), 1184 isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1185 ISC_MSG_POOLFREEMAX, "freemax"), 1186 isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1187 ISC_MSG_POOLFILLCOUNT, "fillcount"), 1188 isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, 1189 ISC_MSG_POOLGETS, "gets"), 1190 "L"); 1191 } 1192 while (pool != NULL) { 1193 fprintf(out, "%15s %10lu %10u %10u %10u %10u %10u %10u %s\n", 1194 pool->name, (unsigned long) pool->size, pool->maxalloc, 1195 pool->allocated, pool->freecount, pool->freemax, 1196 pool->fillcount, pool->gets, 1197 (pool->lock == NULL ? "N" : "Y")); 1198 pool = ISC_LIST_NEXT(pool, link); 1199 } 1200 1201#if ISC_MEM_TRACKLINES 1202 print_active(ctx, out); 1203#endif 1204 1205 UNLOCK(&ctx->lock); 1206} 1207 1208/* 1209 * Replacements for malloc() and free() -- they implicitly remember the 1210 * size of the object allocated (with some additional overhead). 1211 */ 1212 1213static void * 1214isc__mem_allocateunlocked(isc_mem_t *ctx, size_t size) { 1215 size_info *si; 1216 1217 size += ALIGNMENT_SIZE; 1218#if ISC_MEM_USE_INTERNAL_MALLOC 1219 si = mem_getunlocked(ctx, size); 1220#else /* ISC_MEM_USE_INTERNAL_MALLOC */ 1221 si = mem_get(ctx, size); 1222#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 1223 if (si == NULL) 1224 return (NULL); 1225 si->u.size = size; 1226 return (&si[1]); 1227} 1228 1229void * 1230isc__mem_allocate(isc_mem_t *ctx, size_t size FLARG) { 1231 size_info *si; 1232 1233 REQUIRE(VALID_CONTEXT(ctx)); 1234 1235#if ISC_MEM_USE_INTERNAL_MALLOC 1236 LOCK(&ctx->lock); 1237 si = isc__mem_allocateunlocked(ctx, size); 1238#else /* ISC_MEM_USE_INTERNAL_MALLOC */ 1239 si = isc__mem_allocateunlocked(ctx, size); 1240 LOCK(&ctx->lock); 1241 if (si != NULL) 1242 mem_getstats(ctx, si[-1].u.size); 1243#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 1244 1245#if ISC_MEM_TRACKLINES 1246 ADD_TRACE(ctx, si, si[-1].u.size, file, line); 1247#endif 1248 1249 UNLOCK(&ctx->lock); 1250 1251 return (si); 1252} 1253 1254void 1255isc__mem_free(isc_mem_t *ctx, void *ptr FLARG) { 1256 size_info *si; 1257 size_t size; 1258 1259 REQUIRE(VALID_CONTEXT(ctx)); 1260 REQUIRE(ptr != NULL); 1261 1262 si = &(((size_info *)ptr)[-1]); 1263 size = si->u.size; 1264 1265#if ISC_MEM_USE_INTERNAL_MALLOC 1266 LOCK(&ctx->lock); 1267 mem_putunlocked(ctx, si, size); 1268#else /* ISC_MEM_USE_INTERNAL_MALLOC */ 1269 mem_put(ctx, si, size); 1270 LOCK(&ctx->lock); 1271 mem_putstats(ctx, si, size); 1272#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 1273 1274 DELETE_TRACE(ctx, ptr, size, file, line); 1275 1276 UNLOCK(&ctx->lock); 1277} 1278 1279 1280/* 1281 * Other useful things. 1282 */ 1283 1284char * 1285isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) { 1286 size_t len; 1287 char *ns; 1288 1289 REQUIRE(VALID_CONTEXT(mctx)); 1290 REQUIRE(s != NULL); 1291 1292 len = strlen(s); 1293 1294 ns = isc__mem_allocate(mctx, len + 1 FLARG_PASS); 1295 1296 if (ns != NULL) 1297 strncpy(ns, s, len + 1); 1298 1299 return (ns); 1300} 1301 1302void 1303isc_mem_setdestroycheck(isc_mem_t *ctx, isc_boolean_t flag) { 1304 REQUIRE(VALID_CONTEXT(ctx)); 1305 LOCK(&ctx->lock); 1306 1307 ctx->checkfree = flag; 1308 1309 UNLOCK(&ctx->lock); 1310} 1311 1312/* 1313 * Quotas 1314 */ 1315 1316void 1317isc_mem_setquota(isc_mem_t *ctx, size_t quota) { 1318 REQUIRE(VALID_CONTEXT(ctx)); 1319 LOCK(&ctx->lock); 1320 1321 ctx->quota = quota; 1322 1323 UNLOCK(&ctx->lock); 1324} 1325 1326size_t 1327isc_mem_getquota(isc_mem_t *ctx) { 1328 size_t quota; 1329 1330 REQUIRE(VALID_CONTEXT(ctx)); 1331 LOCK(&ctx->lock); 1332 1333 quota = ctx->quota; 1334 1335 UNLOCK(&ctx->lock); 1336 1337 return (quota); 1338} 1339 1340size_t 1341isc_mem_inuse(isc_mem_t *ctx) { 1342 size_t inuse; 1343 1344 REQUIRE(VALID_CONTEXT(ctx)); 1345 LOCK(&ctx->lock); 1346 1347 inuse = ctx->inuse; 1348 1349 UNLOCK(&ctx->lock); 1350 1351 return (inuse); 1352} 1353 1354void 1355isc_mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg, 1356 size_t hiwater, size_t lowater) 1357{ 1358 REQUIRE(VALID_CONTEXT(ctx)); 1359 REQUIRE(hiwater >= lowater); 1360 1361 LOCK(&ctx->lock); 1362 if (water == NULL) { 1363 ctx->water = NULL; 1364 ctx->water_arg = NULL; 1365 ctx->hi_water = 0; 1366 ctx->lo_water = 0; 1367 ctx->hi_called = ISC_FALSE; 1368 } else { 1369 ctx->water = water; 1370 ctx->water_arg = water_arg; 1371 ctx->hi_water = hiwater; 1372 ctx->lo_water = lowater; 1373 ctx->hi_called = ISC_FALSE; 1374 } 1375 UNLOCK(&ctx->lock); 1376} 1377 1378/* 1379 * Memory pool stuff 1380 */ 1381 1382isc_result_t 1383isc_mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp) { 1384 isc_mempool_t *mpctx; 1385 1386 REQUIRE(VALID_CONTEXT(mctx)); 1387 REQUIRE(size > 0U); 1388 REQUIRE(mpctxp != NULL && *mpctxp == NULL); 1389 1390 /* 1391 * Allocate space for this pool, initialize values, and if all works 1392 * well, attach to the memory context. 1393 */ 1394 mpctx = isc_mem_get(mctx, sizeof(isc_mempool_t)); 1395 if (mpctx == NULL) 1396 return (ISC_R_NOMEMORY); 1397 1398 mpctx->magic = MEMPOOL_MAGIC; 1399 mpctx->lock = NULL; 1400 mpctx->mctx = mctx; 1401 mpctx->size = size; 1402 mpctx->maxalloc = UINT_MAX; 1403 mpctx->allocated = 0; 1404 mpctx->freecount = 0; 1405 mpctx->freemax = 1; 1406 mpctx->fillcount = 1; 1407 mpctx->gets = 0; 1408#if ISC_MEMPOOL_NAMES 1409 mpctx->name[0] = 0; 1410#endif 1411 mpctx->items = NULL; 1412 1413 *mpctxp = mpctx; 1414 1415 LOCK(&mctx->lock); 1416 ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link); 1417 UNLOCK(&mctx->lock); 1418 1419 return (ISC_R_SUCCESS); 1420} 1421 1422void 1423isc_mempool_setname(isc_mempool_t *mpctx, const char *name) { 1424 REQUIRE(name != NULL); 1425 1426#if ISC_MEMPOOL_NAMES 1427 if (mpctx->lock != NULL) 1428 LOCK(mpctx->lock); 1429 1430 strncpy(mpctx->name, name, sizeof(mpctx->name) - 1); 1431 mpctx->name[sizeof(mpctx->name) - 1] = '\0'; 1432 1433 if (mpctx->lock != NULL) 1434 UNLOCK(mpctx->lock); 1435#else 1436 UNUSED(mpctx); 1437 UNUSED(name); 1438#endif 1439} 1440 1441void 1442isc_mempool_destroy(isc_mempool_t **mpctxp) { 1443 isc_mempool_t *mpctx; 1444 isc_mem_t *mctx; 1445 isc_mutex_t *lock; 1446 element *item; 1447 1448 REQUIRE(mpctxp != NULL); 1449 mpctx = *mpctxp; 1450 REQUIRE(VALID_MEMPOOL(mpctx)); 1451#if ISC_MEMPOOL_NAMES 1452 if (mpctx->allocated > 0) 1453 UNEXPECTED_ERROR(__FILE__, __LINE__, 1454 "isc_mempool_destroy(): mempool %s " 1455 "leaked memory", 1456 mpctx->name); 1457#endif 1458 REQUIRE(mpctx->allocated == 0); 1459 1460 mctx = mpctx->mctx; 1461 1462 lock = mpctx->lock; 1463 1464 if (lock != NULL) 1465 LOCK(lock); 1466 1467 /* 1468 * Return any items on the free list 1469 */ 1470 LOCK(&mctx->lock); 1471 while (mpctx->items != NULL) { 1472 INSIST(mpctx->freecount > 0); 1473 mpctx->freecount--; 1474 item = mpctx->items; 1475 mpctx->items = item->next; 1476 1477#if ISC_MEM_USE_INTERNAL_MALLOC 1478 mem_putunlocked(mctx, item, mpctx->size); 1479#else /* ISC_MEM_USE_INTERNAL_MALLOC */ 1480 mem_put(mctx, item, mpctx->size); 1481 mem_putstats(mctx, item, mpctx->size); 1482#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 1483 } 1484 UNLOCK(&mctx->lock); 1485 1486 /* 1487 * Remove our linked list entry from the memory context. 1488 */ 1489 LOCK(&mctx->lock); 1490 ISC_LIST_UNLINK(mctx->pools, mpctx, link); 1491 UNLOCK(&mctx->lock); 1492 1493 mpctx->magic = 0; 1494 1495 isc_mem_put(mpctx->mctx, mpctx, sizeof(isc_mempool_t)); 1496 1497 if (lock != NULL) 1498 UNLOCK(lock); 1499 1500 *mpctxp = NULL; 1501} 1502 1503void 1504isc_mempool_associatelock(isc_mempool_t *mpctx, isc_mutex_t *lock) { 1505 REQUIRE(VALID_MEMPOOL(mpctx)); 1506 REQUIRE(mpctx->lock == NULL); 1507 REQUIRE(lock != NULL); 1508 1509 mpctx->lock = lock; 1510} 1511 1512void * 1513isc__mempool_get(isc_mempool_t *mpctx FLARG) { 1514 element *item; 1515 isc_mem_t *mctx; 1516 unsigned int i; 1517 1518 REQUIRE(VALID_MEMPOOL(mpctx)); 1519 1520 mctx = mpctx->mctx; 1521 1522 if (mpctx->lock != NULL) 1523 LOCK(mpctx->lock); 1524 1525 /* 1526 * Don't let the caller go over quota 1527 */ 1528 if (mpctx->allocated >= mpctx->maxalloc) { 1529 item = NULL; 1530 goto out; 1531 } 1532 1533 /* 1534 * if we have a free list item, return the first here 1535 */ 1536 item = mpctx->items; 1537 if (item != NULL) { 1538 mpctx->items = item->next; 1539 INSIST(mpctx->freecount > 0); 1540 mpctx->freecount--; 1541 mpctx->gets++; 1542 mpctx->allocated++; 1543 goto out; 1544 } 1545 1546 /* 1547 * We need to dip into the well. Lock the memory context here and 1548 * fill up our free list. 1549 */ 1550 LOCK(&mctx->lock); 1551 for (i = 0; i < mpctx->fillcount; i++) { 1552#if ISC_MEM_USE_INTERNAL_MALLOC 1553 item = mem_getunlocked(mctx, mpctx->size); 1554#else /* ISC_MEM_USE_INTERNAL_MALLOC */ 1555 item = mem_get(mctx, mpctx->size); 1556 if (item != NULL) 1557 mem_getstats(mctx, mpctx->size); 1558#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 1559 if (item == NULL) 1560 break; 1561 item->next = mpctx->items; 1562 mpctx->items = item; 1563 mpctx->freecount++; 1564 } 1565 UNLOCK(&mctx->lock); 1566 1567 /* 1568 * If we didn't get any items, return NULL. 1569 */ 1570 item = mpctx->items; 1571 if (item == NULL) 1572 goto out; 1573 1574 mpctx->items = item->next; 1575 mpctx->freecount--; 1576 mpctx->gets++; 1577 mpctx->allocated++; 1578 1579 out: 1580 if (mpctx->lock != NULL) 1581 UNLOCK(mpctx->lock); 1582 1583#if ISC_MEM_TRACKLINES 1584 if (item != NULL) { 1585 LOCK(&mctx->lock); 1586 ADD_TRACE(mctx, item, mpctx->size, file, line); 1587 UNLOCK(&mctx->lock); 1588 } 1589#endif /* ISC_MEM_TRACKLINES */ 1590 1591 return (item); 1592} 1593 1594void 1595isc__mempool_put(isc_mempool_t *mpctx, void *mem FLARG) { 1596 isc_mem_t *mctx; 1597 element *item; 1598 1599 REQUIRE(VALID_MEMPOOL(mpctx)); 1600 REQUIRE(mem != NULL); 1601 1602 mctx = mpctx->mctx; 1603 1604 if (mpctx->lock != NULL) 1605 LOCK(mpctx->lock); 1606 1607 INSIST(mpctx->allocated > 0); 1608 mpctx->allocated--; 1609 1610#if ISC_MEM_TRACKLINES 1611 LOCK(&mctx->lock); 1612 DELETE_TRACE(mctx, mem, mpctx->size, file, line); 1613 UNLOCK(&mctx->lock); 1614#endif /* ISC_MEM_TRACKLINES */ 1615 1616 /* 1617 * If our free list is full, return this to the mctx directly. 1618 */ 1619 if (mpctx->freecount >= mpctx->freemax) { 1620#if ISC_MEM_USE_INTERNAL_MALLOC 1621 LOCK(&mctx->lock); 1622 mem_putunlocked(mctx, mem, mpctx->size); 1623 UNLOCK(&mctx->lock); 1624#else /* ISC_MEM_USE_INTERNAL_MALLOC */ 1625 mem_put(mctx, mem, mpctx->size); 1626 LOCK(&mctx->lock); 1627 mem_putstats(mctx, mem, mpctx->size); 1628 UNLOCK(&mctx->lock); 1629#endif /* ISC_MEM_USE_INTERNAL_MALLOC */ 1630 if (mpctx->lock != NULL) 1631 UNLOCK(mpctx->lock); 1632 return; 1633 } 1634 1635 /* 1636 * Otherwise, attach it to our free list and bump the counter. 1637 */ 1638 mpctx->freecount++; 1639 item = (element *)mem; 1640 item->next = mpctx->items; 1641 mpctx->items = item; 1642 1643 if (mpctx->lock != NULL) 1644 UNLOCK(mpctx->lock); 1645} 1646 1647/* 1648 * Quotas 1649 */ 1650 1651void 1652isc_mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit) { 1653 REQUIRE(VALID_MEMPOOL(mpctx)); 1654 1655 if (mpctx->lock != NULL) 1656 LOCK(mpctx->lock); 1657 1658 mpctx->freemax = limit; 1659 1660 if (mpctx->lock != NULL) 1661 UNLOCK(mpctx->lock); 1662} 1663 1664unsigned int 1665isc_mempool_getfreemax(isc_mempool_t *mpctx) { 1666 unsigned int freemax; 1667 1668 REQUIRE(VALID_MEMPOOL(mpctx)); 1669 1670 if (mpctx->lock != NULL) 1671 LOCK(mpctx->lock); 1672 1673 freemax = mpctx->freemax; 1674 1675 if (mpctx->lock != NULL) 1676 UNLOCK(mpctx->lock); 1677 1678 return (freemax); 1679} 1680 1681unsigned int 1682isc_mempool_getfreecount(isc_mempool_t *mpctx) { 1683 unsigned int freecount; 1684 1685 REQUIRE(VALID_MEMPOOL(mpctx)); 1686 1687 if (mpctx->lock != NULL) 1688 LOCK(mpctx->lock); 1689 1690 freecount = mpctx->freecount; 1691 1692 if (mpctx->lock != NULL) 1693 UNLOCK(mpctx->lock); 1694 1695 return (freecount); 1696} 1697 1698void 1699isc_mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit) { 1700 REQUIRE(limit > 0); 1701 1702 REQUIRE(VALID_MEMPOOL(mpctx)); 1703 1704 if (mpctx->lock != NULL) 1705 LOCK(mpctx->lock); 1706 1707 mpctx->maxalloc = limit; 1708 1709 if (mpctx->lock != NULL) 1710 UNLOCK(mpctx->lock); 1711} 1712 1713unsigned int 1714isc_mempool_getmaxalloc(isc_mempool_t *mpctx) { 1715 unsigned int maxalloc; 1716 1717 REQUIRE(VALID_MEMPOOL(mpctx)); 1718 1719 if (mpctx->lock != NULL) 1720 LOCK(mpctx->lock); 1721 1722 maxalloc = mpctx->maxalloc; 1723 1724 if (mpctx->lock != NULL) 1725 UNLOCK(mpctx->lock); 1726 1727 return (maxalloc); 1728} 1729 1730unsigned int 1731isc_mempool_getallocated(isc_mempool_t *mpctx) { 1732 unsigned int allocated; 1733 1734 REQUIRE(VALID_MEMPOOL(mpctx)); 1735 1736 if (mpctx->lock != NULL) 1737 LOCK(mpctx->lock); 1738 1739 allocated = mpctx->allocated; 1740 1741 if (mpctx->lock != NULL) 1742 UNLOCK(mpctx->lock); 1743 1744 return (allocated); 1745} 1746 1747void 1748isc_mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit) { 1749 REQUIRE(limit > 0); 1750 REQUIRE(VALID_MEMPOOL(mpctx)); 1751 1752 if (mpctx->lock != NULL) 1753 LOCK(mpctx->lock); 1754 1755 mpctx->fillcount = limit; 1756 1757 if (mpctx->lock != NULL) 1758 UNLOCK(mpctx->lock); 1759} 1760 1761unsigned int 1762isc_mempool_getfillcount(isc_mempool_t *mpctx) { 1763 unsigned int fillcount; 1764 1765 REQUIRE(VALID_MEMPOOL(mpctx)); 1766 1767 if (mpctx->lock != NULL) 1768 LOCK(mpctx->lock); 1769 1770 fillcount = mpctx->fillcount; 1771 1772 if (mpctx->lock != NULL) 1773 UNLOCK(mpctx->lock); 1774 1775 return (fillcount); 1776} 1777