1320731Szbb/* 2368013Smw * Copyright (c) 2000-2004 Proofpoint, Inc. and its suppliers. 3320731Szbb * All rights reserved. 4361534Smw * 5320731Szbb * By using this file, you agree to the terms and conditions set 6320731Szbb * forth in the LICENSE file which can be found at the top level of 7320731Szbb * the sendmail distribution. 8320731Szbb */ 9320731Szbb 10320731Szbb#include <sm/gen.h> 11320731SzbbSM_RCSID("@(#)$Id: rpool.c,v 1.29 2013-11-22 20:51:43 ca Exp $") 12320731Szbb 13320731Szbb/* 14320731Szbb** resource pools 15320731Szbb** For documentation, see rpool.html 16320731Szbb*/ 17320731Szbb 18320731Szbb#include <sm/exc.h> 19320731Szbb#include <sm/heap.h> 20320731Szbb#include <sm/rpool.h> 21320731Szbb#include <sm/varargs.h> 22320731Szbb#include <sm/conf.h> 23320731Szbb#if _FFR_PERF_RPOOL 24320731Szbb# include <syslog.h> 25320731Szbb#endif 26320731Szbb 27320731Szbbconst char SmRpoolMagic[] = "sm_rpool"; 28320731Szbb 29320731Szbbtypedef union 30320731Szbb{ 31320731Szbb SM_POOLLINK_T link; 32320731Szbb char align[SM_ALIGN_SIZE]; 33320731Szbb} SM_POOLHDR_T; 34320731Szbb 35320731Szbbstatic char *sm_rpool_allocblock_x __P((SM_RPOOL_T *, size_t)); 36320731Szbbstatic char *sm_rpool_allocblock __P((SM_RPOOL_T *, size_t)); 37320731Szbb 38320731Szbb/* 39320731Szbb** Tune this later 40320731Szbb*/ 41320731Szbb 42320731Szbb#define POOLSIZE 4096 43320731Szbb#define BIG_OBJECT_RATIO 10 44320731Szbb 45320731Szbb/* 46320731Szbb** SM_RPOOL_ALLOCBLOCK_X -- allocate a new block for an rpool. 47320731Szbb** 48320731Szbb** Parameters: 49320731Szbb** rpool -- rpool to which the block should be added. 50320731Szbb** size -- size of block. 51320731Szbb** 52320731Szbb** Returns: 53320731Szbb** Pointer to block. 54320731Szbb** 55320731Szbb** Exceptions: 56320731Szbb** F:sm_heap -- out of memory 57320731Szbb*/ 58320731Szbb 59320731Szbbstatic char * 60320731Szbbsm_rpool_allocblock_x(rpool, size) 61320731Szbb SM_RPOOL_T *rpool; 62320731Szbb size_t size; 63320731Szbb{ 64320731Szbb SM_POOLLINK_T *p; 65320731Szbb 66320731Szbb p = sm_malloc_x(sizeof(SM_POOLHDR_T) + size); 67320731Szbb p->sm_pnext = rpool->sm_pools; 68320731Szbb rpool->sm_pools = p; 69320731Szbb return (char*) p + sizeof(SM_POOLHDR_T); 70320731Szbb} 71320731Szbb 72320731Szbb/* 73320731Szbb** SM_RPOOL_ALLOCBLOCK -- allocate a new block for an rpool. 74361467Smw** 75320731Szbb** Parameters: 76320731Szbb** rpool -- rpool to which the block should be added. 77320731Szbb** size -- size of block. 78320731Szbb** 79320731Szbb** Returns: 80361534Smw** Pointer to block, NULL on failure. 81320731Szbb*/ 82320731Szbb 83320731Szbbstatic char * 84320731Szbbsm_rpool_allocblock(rpool, size) 85320731Szbb SM_RPOOL_T *rpool; 86320731Szbb size_t size; 87320731Szbb{ 88320731Szbb SM_POOLLINK_T *p; 89320731Szbb 90320731Szbb p = sm_malloc(sizeof(SM_POOLHDR_T) + size); 91320731Szbb if (p == NULL) 92320731Szbb return NULL; 93320731Szbb p->sm_pnext = rpool->sm_pools; 94320731Szbb rpool->sm_pools = p; 95361467Smw return (char*) p + sizeof(SM_POOLHDR_T); 96320731Szbb} 97320731Szbb 98320731Szbb/* 99320731Szbb** SM_RPOOL_MALLOC_TAGGED_X -- allocate memory from rpool 100320731Szbb** 101320731Szbb** Parameters: 102320731Szbb** rpool -- rpool from which memory should be allocated; 103361534Smw** can be NULL, use sm_malloc() then. 104320731Szbb** size -- size of block. 105320731Szbb** file -- filename. 106320731Szbb** line -- line number in file. 107320731Szbb** group -- heap group for debugging. 108320731Szbb** 109320731Szbb** Returns: 110320731Szbb** Pointer to block. 111320731Szbb** 112320731Szbb** Exceptions: 113320731Szbb** F:sm_heap -- out of memory 114343397Smw** 115343397Smw** Notice: XXX 116343397Smw** if size == 0 and the rpool is new (no memory 117343397Smw** allocated yet) NULL is returned! 118343397Smw** We could solve this by 119343397Smw** - wasting 1 byte (size < avail) 120343397Smw** - checking for rpool->sm_poolptr != NULL 121361534Smw** - not asking for 0 sized buffer 122343397Smw*/ 123343397Smw 124343397Smwvoid * 125343397Smw#if SM_HEAP_CHECK 126343397Smwsm_rpool_malloc_tagged_x(rpool, size, file, line, group) 127343397Smw SM_RPOOL_T *rpool; 128343397Smw size_t size; 129343397Smw char *file; 130361534Smw int line; 131343397Smw int group; 132343397Smw#else /* SM_HEAP_CHECK */ 133361467Smwsm_rpool_malloc_x(rpool, size) 134361467Smw SM_RPOOL_T *rpool; 135361467Smw size_t size; 136361467Smw#endif /* SM_HEAP_CHECK */ 137361467Smw{ 138361467Smw char *ptr; 139361467Smw 140361467Smw if (rpool == NULL) 141361467Smw return sm_malloc_tagged_x(size, file, line, group); 142361467Smw 143361467Smw /* Ensure that size is properly aligned. */ 144361467Smw if (size & SM_ALIGN_BITS) 145361467Smw size = (size & ~SM_ALIGN_BITS) + SM_ALIGN_SIZE; 146361467Smw 147361467Smw /* The common case. This is optimized for speed. */ 148361467Smw if (size <= rpool->sm_poolavail) 149361467Smw { 150361467Smw ptr = rpool->sm_poolptr; 151361467Smw rpool->sm_poolptr += size; 152361467Smw rpool->sm_poolavail -= size; 153361467Smw return ptr; 154361467Smw } 155361467Smw 156361467Smw /* 157361467Smw ** The slow case: we need to call malloc. 158361467Smw ** The SM_REQUIRE assertion is deferred until now, for speed. 159361467Smw ** That's okay: we set rpool->sm_poolavail to 0 when we free an rpool, 160361467Smw ** so the common case code won't be triggered on a dangling pointer. 161361467Smw */ 162361467Smw 163361467Smw SM_REQUIRE(rpool->sm_magic == SmRpoolMagic); 164361534Smw 165361534Smw /* 166361467Smw ** If size > sm_poolsize, then malloc a new block especially for 167361467Smw ** this request. Future requests will be allocated from the 168361467Smw ** current pool. 169361467Smw ** 170361467Smw ** What if the current pool is mostly unallocated, and the current 171361467Smw ** request is larger than the available space, but < sm_poolsize? 172361467Smw ** If we discard the current pool, and start allocating from a new 173361467Smw ** pool, then we will be wasting a lot of space. For this reason, 174368013Smw ** we malloc a block just for the current request if size > 175368013Smw ** sm_bigobjectsize, where sm_bigobjectsize <= sm_poolsize. 176361467Smw ** Thus, the most space that we will waste at the end of a pool 177361467Smw ** is sm_bigobjectsize - 1. 178361467Smw */ 179361467Smw 180361467Smw if (size > rpool->sm_bigobjectsize) 181320731Szbb { 182320731Szbb#if _FFR_PERF_RPOOL 183361534Smw ++rpool->sm_nbigblocks; 184361467Smw#endif 185320731Szbb return sm_rpool_allocblock_x(rpool, size); 186368013Smw } 187368013Smw SM_ASSERT(rpool->sm_bigobjectsize <= rpool->sm_poolsize); 188320731Szbb ptr = sm_rpool_allocblock_x(rpool, rpool->sm_poolsize); 189320731Szbb rpool->sm_poolptr = ptr + size; 190320731Szbb rpool->sm_poolavail = rpool->sm_poolsize - size; 191320731Szbb#if _FFR_PERF_RPOOL 192361467Smw ++rpool->sm_npools; 193368013Smw#endif 194368013Smw return ptr; 195368013Smw} 196361467Smw 197361467Smw/* 198361467Smw** SM_RPOOL_MALLOC_TAGGED -- allocate memory from rpool 199320731Szbb** 200320731Szbb** Parameters: 201320731Szbb** rpool -- rpool from which memory should be allocated; 202320731Szbb** can be NULL, use sm_malloc() then. 203320731Szbb** size -- size of block. 204320731Szbb** file -- filename. 205320731Szbb** line -- line number in file. 206320731Szbb** group -- heap group for debugging. 207361534Smw** 208361534Smw** Returns: 209361534Smw** Pointer to block, NULL on failure. 210361534Smw** 211320731Szbb** Notice: XXX 212361534Smw** if size == 0 and the rpool is new (no memory 213368013Smw** allocated yet) NULL is returned! 214368013Smw** We could solve this by 215361534Smw** - wasting 1 byte (size < avail) 216361534Smw** - checking for rpool->sm_poolptr != NULL 217361534Smw** - not asking for 0 sized buffer 218361534Smw*/ 219320731Szbb 220320731Szbbvoid * 221320731Szbb#if SM_HEAP_CHECK 222320731Szbbsm_rpool_malloc_tagged(rpool, size, file, line, group) 223320731Szbb SM_RPOOL_T *rpool; 224320731Szbb size_t size; 225320731Szbb char *file; 226320731Szbb int line; 227320731Szbb int group; 228320731Szbb#else /* SM_HEAP_CHECK */ 229320731Szbbsm_rpool_malloc(rpool, size) 230320731Szbb SM_RPOOL_T *rpool; 231320731Szbb size_t size; 232320731Szbb#endif /* SM_HEAP_CHECK */ 233320731Szbb{ 234320731Szbb char *ptr; 235320731Szbb 236320731Szbb if (rpool == NULL) 237320731Szbb return sm_malloc_tagged(size, file, line, group); 238320731Szbb 239320731Szbb /* Ensure that size is properly aligned. */ 240320731Szbb if (size & SM_ALIGN_BITS) 241320731Szbb size = (size & ~SM_ALIGN_BITS) + SM_ALIGN_SIZE; 242320731Szbb 243361467Smw /* The common case. This is optimized for speed. */ 244361467Smw if (size <= rpool->sm_poolavail) 245361467Smw { 246361467Smw ptr = rpool->sm_poolptr; 247361467Smw rpool->sm_poolptr += size; 248361467Smw rpool->sm_poolavail -= size; 249361467Smw return ptr; 250361467Smw } 251361467Smw 252361467Smw /* 253361467Smw ** The slow case: we need to call malloc. 254361467Smw ** The SM_REQUIRE assertion is deferred until now, for speed. 255361467Smw ** That's okay: we set rpool->sm_poolavail to 0 when we free an rpool, 256361467Smw ** so the common case code won't be triggered on a dangling pointer. 257361467Smw */ 258361467Smw 259361467Smw SM_REQUIRE(rpool->sm_magic == SmRpoolMagic); 260361467Smw 261361467Smw /* 262361467Smw ** If size > sm_poolsize, then malloc a new block especially for 263361467Smw ** this request. Future requests will be allocated from the 264361467Smw ** current pool. 265361467Smw ** 266361467Smw ** What if the current pool is mostly unallocated, and the current 267361467Smw ** request is larger than the available space, but < sm_poolsize? 268361467Smw ** If we discard the current pool, and start allocating from a new 269361467Smw ** pool, then we will be wasting a lot of space. For this reason, 270361467Smw ** we malloc a block just for the current request if size > 271361467Smw ** sm_bigobjectsize, where sm_bigobjectsize <= sm_poolsize. 272361467Smw ** Thus, the most space that we will waste at the end of a pool 273361467Smw ** is sm_bigobjectsize - 1. 274361467Smw */ 275361467Smw 276361467Smw if (size > rpool->sm_bigobjectsize) 277361467Smw { 278368013Smw#if _FFR_PERF_RPOOL 279368013Smw ++rpool->sm_nbigblocks; 280361467Smw#endif 281361467Smw return sm_rpool_allocblock(rpool, size); 282361467Smw } 283361467Smw SM_ASSERT(rpool->sm_bigobjectsize <= rpool->sm_poolsize); 284361467Smw ptr = sm_rpool_allocblock(rpool, rpool->sm_poolsize); 285361467Smw if (ptr == NULL) 286361467Smw return NULL; 287361467Smw rpool->sm_poolptr = ptr + size; 288320731Szbb rpool->sm_poolavail = rpool->sm_poolsize - size; 289320731Szbb#if _FFR_PERF_RPOOL 290320731Szbb ++rpool->sm_npools; 291320731Szbb#endif 292 return ptr; 293} 294 295/* 296** SM_RPOOL_NEW_X -- create a new rpool. 297** 298** Parameters: 299** parent -- pointer to parent rpool, can be NULL. 300** 301** Returns: 302** Pointer to new rpool. 303*/ 304 305SM_RPOOL_T * 306sm_rpool_new_x(parent) 307 SM_RPOOL_T *parent; 308{ 309 SM_RPOOL_T *rpool; 310 311 rpool = sm_malloc_x(sizeof(SM_RPOOL_T)); 312 if (parent == NULL) 313 rpool->sm_parentlink = NULL; 314 else 315 { 316 SM_TRY 317 rpool->sm_parentlink = sm_rpool_attach_x(parent, 318 (SM_RPOOL_RFREE_T) sm_rpool_free, 319 (void *) rpool); 320 SM_EXCEPT(exc, "*") 321 sm_free(rpool); 322 sm_exc_raise_x(exc); 323 SM_END_TRY 324 } 325 rpool->sm_magic = SmRpoolMagic; 326 327 rpool->sm_poolsize = POOLSIZE - sizeof(SM_POOLHDR_T); 328 rpool->sm_bigobjectsize = rpool->sm_poolsize / BIG_OBJECT_RATIO; 329 rpool->sm_poolptr = NULL; 330 rpool->sm_poolavail = 0; 331 rpool->sm_pools = NULL; 332 333 rpool->sm_rptr = NULL; 334 rpool->sm_ravail = 0; 335 rpool->sm_rlists = NULL; 336#if _FFR_PERF_RPOOL 337 rpool->sm_nbigblocks = 0; 338 rpool->sm_npools = 0; 339#endif 340 341 return rpool; 342} 343 344/* 345** SM_RPOOL_SETSIZES -- set sizes for rpool. 346** 347** Parameters: 348** poolsize -- size of a single rpool block. 349** bigobjectsize -- if this size is exceeded, an individual 350** block is allocated (must be less or equal poolsize). 351** 352** Returns: 353** none. 354*/ 355 356void 357sm_rpool_setsizes(rpool, poolsize, bigobjectsize) 358 SM_RPOOL_T *rpool; 359 size_t poolsize; 360 size_t bigobjectsize; 361{ 362 SM_REQUIRE(poolsize >= bigobjectsize); 363 if (poolsize == 0) 364 poolsize = POOLSIZE - sizeof(SM_POOLHDR_T); 365 if (bigobjectsize == 0) 366 bigobjectsize = poolsize / BIG_OBJECT_RATIO; 367 rpool->sm_poolsize = poolsize; 368 rpool->sm_bigobjectsize = bigobjectsize; 369} 370 371/* 372** SM_RPOOL_FREE -- free an rpool and release all of its resources. 373** 374** Parameters: 375** rpool -- rpool to free. 376** 377** Returns: 378** none. 379*/ 380 381void 382sm_rpool_free(rpool) 383 SM_RPOOL_T *rpool; 384{ 385 SM_RLIST_T *rl, *rnext; 386 SM_RESOURCE_T *r, *rmax; 387 SM_POOLLINK_T *pp, *pnext; 388 389 if (rpool == NULL) 390 return; 391 392 /* 393 ** It's important to free the resources before the memory pools, 394 ** because the resource free functions might modify the contents 395 ** of the memory pools. 396 */ 397 398 rl = rpool->sm_rlists; 399 if (rl != NULL) 400 { 401 rmax = rpool->sm_rptr; 402 for (;;) 403 { 404 for (r = rl->sm_rvec; r < rmax; ++r) 405 { 406 if (r->sm_rfree != NULL) 407 r->sm_rfree(r->sm_rcontext); 408 } 409 rnext = rl->sm_rnext; 410 sm_free(rl); 411 if (rnext == NULL) 412 break; 413 rl = rnext; 414 rmax = &rl->sm_rvec[SM_RLIST_MAX]; 415 } 416 } 417 418 /* 419 ** Now free the memory pools. 420 */ 421 422 for (pp = rpool->sm_pools; pp != NULL; pp = pnext) 423 { 424 pnext = pp->sm_pnext; 425 sm_free(pp); 426 } 427 428 /* 429 ** Disconnect rpool from its parent. 430 */ 431 432 if (rpool->sm_parentlink != NULL) 433 *rpool->sm_parentlink = NULL; 434 435 /* 436 ** Setting these fields to zero means that any future to attempt 437 ** to use the rpool after it is freed will cause an assertion failure. 438 */ 439 440 rpool->sm_magic = NULL; 441 rpool->sm_poolavail = 0; 442 rpool->sm_ravail = 0; 443 444#if _FFR_PERF_RPOOL 445 if (rpool->sm_nbigblocks > 0 || rpool->sm_npools > 1) 446 syslog(LOG_NOTICE, 447 "perf: rpool=%lx, sm_nbigblocks=%d, sm_npools=%d", 448 (long) rpool, rpool->sm_nbigblocks, rpool->sm_npools); 449 rpool->sm_nbigblocks = 0; 450 rpool->sm_npools = 0; 451#endif /* _FFR_PERF_RPOOL */ 452 sm_free(rpool); 453} 454 455/* 456** SM_RPOOL_ATTACH_X -- attach a resource to an rpool. 457** 458** Parameters: 459** rpool -- rpool to which resource should be attached. 460** rfree -- function to call when rpool is freed. 461** rcontext -- argument for function to call when rpool is freed. 462** 463** Returns: 464** Pointer to allocated function. 465** 466** Exceptions: 467** F:sm_heap -- out of memory 468*/ 469 470SM_RPOOL_ATTACH_T 471sm_rpool_attach_x(rpool, rfree, rcontext) 472 SM_RPOOL_T *rpool; 473 SM_RPOOL_RFREE_T rfree; 474 void *rcontext; 475{ 476 SM_RLIST_T *rl; 477 SM_RPOOL_ATTACH_T a; 478 479 SM_REQUIRE_ISA(rpool, SmRpoolMagic); 480 481 if (rpool->sm_ravail == 0) 482 { 483 rl = sm_malloc_x(sizeof(SM_RLIST_T)); 484 rl->sm_rnext = rpool->sm_rlists; 485 rpool->sm_rlists = rl; 486 rpool->sm_rptr = rl->sm_rvec; 487 rpool->sm_ravail = SM_RLIST_MAX; 488 } 489 490 a = &rpool->sm_rptr->sm_rfree; 491 rpool->sm_rptr->sm_rfree = rfree; 492 rpool->sm_rptr->sm_rcontext = rcontext; 493 ++rpool->sm_rptr; 494 --rpool->sm_ravail; 495 return a; 496} 497 498#if DO_NOT_USE_STRCPY 499/* 500** SM_RPOOL_STRDUP_X -- Create a copy of a C string 501** 502** Parameters: 503** rpool -- rpool to use. 504** s -- the string to copy. 505** 506** Returns: 507** pointer to newly allocated string. 508*/ 509 510char * 511sm_rpool_strdup_x(rpool, s) 512 SM_RPOOL_T *rpool; 513 const char *s; 514{ 515 size_t l; 516 char *n; 517 518 l = strlen(s); 519 SM_ASSERT(l + 1 > l); 520 n = sm_rpool_malloc_x(rpool, l + 1); 521 sm_strlcpy(n, s, l + 1); 522 return n; 523} 524#endif /* DO_NOT_USE_STRCPY */ 525