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