1/* 2 * Copyright (c) 2004-2006 Voltaire, Inc. All rights reserved. 3 * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved. 4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved. 5 * 6 * This software is available to you under a choice of one of two 7 * licenses. You may choose to be licensed under the terms of the GNU 8 * General Public License (GPL) Version 2, available from the file 9 * COPYING in the main directory of this source tree, or the 10 * OpenIB.org BSD license below: 11 * 12 * Redistribution and use in source and binary forms, with or 13 * without modification, are permitted provided that the following 14 * conditions are met: 15 * 16 * - Redistributions of source code must retain the above 17 * copyright notice, this list of conditions and the following 18 * disclaimer. 19 * 20 * - Redistributions in binary form must reproduce the above 21 * copyright notice, this list of conditions and the following 22 * disclaimer in the documentation and/or other materials 23 * provided with the distribution. 24 * 25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 * SOFTWARE. 33 * 34 */ 35 36/* 37 * Abstract: 38 * Implementation of the grow pools. The grow pools manage a pool of objects. 39 * The pools can grow to meet demand, limited only by system memory. 40 * 41 */ 42 43#if HAVE_CONFIG_H 44# include <config.h> 45#endif /* HAVE_CONFIG_H */ 46 47#include <stdlib.h> 48#include <string.h> 49#include <complib/cl_qcomppool.h> 50#include <complib/cl_comppool.h> 51#include <complib/cl_qpool.h> 52#include <complib/cl_pool.h> 53#include <complib/cl_math.h> 54 55/* 56 * IMPLEMENTATION OF QUICK COMPOSITE POOL 57 */ 58void cl_qcpool_construct(IN cl_qcpool_t * const p_pool) 59{ 60 CL_ASSERT(p_pool); 61 62 memset(p_pool, 0, sizeof(cl_qcpool_t)); 63 64 p_pool->state = CL_UNINITIALIZED; 65} 66 67cl_status_t 68cl_qcpool_init(IN cl_qcpool_t * const p_pool, 69 IN const size_t min_size, 70 IN const size_t max_size, 71 IN const size_t grow_size, 72 IN const size_t * const component_sizes, 73 IN const uint32_t num_components, 74 IN cl_pfn_qcpool_init_t pfn_initializer OPTIONAL, 75 IN cl_pfn_qcpool_dtor_t pfn_destructor OPTIONAL, 76 IN const void *const context) 77{ 78 cl_status_t status; 79 uint32_t i; 80 81 CL_ASSERT(p_pool); 82 /* Must have a minimum of 1 component. */ 83 CL_ASSERT(num_components); 84 /* A component size array is required. */ 85 CL_ASSERT(component_sizes); 86 /* 87 * If no initializer is provided, the first component must be large 88 * enough to hold a pool item. 89 */ 90 CL_ASSERT(pfn_initializer || 91 (component_sizes[0] >= sizeof(cl_pool_item_t))); 92 93 cl_qcpool_construct(p_pool); 94 95 if (num_components > 1 && !pfn_initializer) 96 return (CL_INVALID_SETTING); 97 98 if (max_size && max_size < min_size) 99 return (CL_INVALID_SETTING); 100 101 /* 102 * Allocate the array of component sizes and component pointers all 103 * in one allocation. 104 */ 105 p_pool->component_sizes = (size_t *) malloc((sizeof(size_t) + 106 sizeof(void *)) * 107 num_components); 108 109 if (!p_pool->component_sizes) 110 return (CL_INSUFFICIENT_MEMORY); 111 else 112 memset(p_pool->component_sizes, 0, 113 (sizeof(size_t) + sizeof(void *)) * num_components); 114 115 /* Calculate the pointer to the array of pointers, used for callbacks. */ 116 p_pool->p_components = 117 (void **)(p_pool->component_sizes + num_components); 118 119 /* Copy the user's sizes into our array for future use. */ 120 memcpy(p_pool->component_sizes, component_sizes, 121 sizeof(component_sizes[0]) * num_components); 122 123 /* Store the number of components per object. */ 124 p_pool->num_components = num_components; 125 126 /* Round up and store the size of the components. */ 127 for (i = 0; i < num_components; i++) { 128 /* 129 * We roundup each component size so that all components 130 * are aligned on a natural boundary. 131 */ 132 p_pool->component_sizes[i] = 133 ROUNDUP(p_pool->component_sizes[i], sizeof(uintn_t)); 134 } 135 136 p_pool->max_objects = max_size ? max_size : ~(size_t) 0; 137 p_pool->grow_size = grow_size; 138 139 /* Store callback function pointers. */ 140 p_pool->pfn_init = pfn_initializer; /* may be NULL */ 141 p_pool->pfn_dtor = pfn_destructor; /* may be NULL */ 142 p_pool->context = context; 143 144 cl_qlist_init(&p_pool->alloc_list); 145 146 cl_qlist_init(&p_pool->free_list); 147 148 /* 149 * We are now initialized. We change the initialized flag before 150 * growing since the grow function asserts that we are initialized. 151 */ 152 p_pool->state = CL_INITIALIZED; 153 154 /* Allocate the minimum number of objects as requested. */ 155 if (!min_size) 156 return (CL_SUCCESS); 157 158 status = cl_qcpool_grow(p_pool, min_size); 159 /* Trap for error and cleanup if necessary. */ 160 if (status != CL_SUCCESS) 161 cl_qcpool_destroy(p_pool); 162 163 return (status); 164} 165 166void cl_qcpool_destroy(IN cl_qcpool_t * const p_pool) 167{ 168 /* CL_ASSERT that a non-NULL pointer was provided. */ 169 CL_ASSERT(p_pool); 170 /* CL_ASSERT that we are in a valid state (not uninitialized memory). */ 171 CL_ASSERT(cl_is_state_valid(p_pool->state)); 172 173 if (p_pool->state == CL_INITIALIZED) { 174 /* 175 * Assert if the user hasn't put everything back in the pool 176 * before destroying it 177 * if they haven't, then most likely they are still using memory 178 * that will be freed, and the destructor will not be called! 179 */ 180#ifdef _DEBUG_ 181 /* but we do not want "free" version to assert on this one */ 182 CL_ASSERT(cl_qcpool_count(p_pool) == p_pool->num_objects); 183#endif 184 /* call the user's destructor for each object in the pool */ 185 if (p_pool->pfn_dtor) { 186 while (!cl_is_qlist_empty(&p_pool->free_list)) { 187 p_pool->pfn_dtor((cl_pool_item_t *) 188 cl_qlist_remove_head(&p_pool-> 189 free_list), 190 (void *)p_pool->context); 191 } 192 } else { 193 cl_qlist_remove_all(&p_pool->free_list); 194 } 195 196 /* Free all allocated memory blocks. */ 197 while (!cl_is_qlist_empty(&p_pool->alloc_list)) 198 free(cl_qlist_remove_head(&p_pool->alloc_list)); 199 200 if (p_pool->component_sizes) { 201 free(p_pool->component_sizes); 202 p_pool->component_sizes = NULL; 203 } 204 } 205 206 p_pool->state = CL_UNINITIALIZED; 207} 208 209cl_status_t cl_qcpool_grow(IN cl_qcpool_t * const p_pool, IN size_t obj_count) 210{ 211 cl_status_t status = CL_SUCCESS; 212 uint8_t *p_objects; 213 cl_pool_item_t *p_pool_item; 214 uint32_t i; 215 size_t obj_size; 216 217 CL_ASSERT(p_pool); 218 CL_ASSERT(p_pool->state == CL_INITIALIZED); 219 CL_ASSERT(obj_count); 220 221 /* Validate that growth is possible. */ 222 if (p_pool->num_objects == p_pool->max_objects) 223 return (CL_INSUFFICIENT_MEMORY); 224 225 /* Cap the growth to the desired maximum. */ 226 if (obj_count > (p_pool->max_objects - p_pool->num_objects)) 227 obj_count = p_pool->max_objects - p_pool->num_objects; 228 229 /* Calculate the size of an object. */ 230 obj_size = 0; 231 for (i = 0; i < p_pool->num_components; i++) 232 obj_size += p_pool->component_sizes[i]; 233 234 /* Allocate the buffer for the new objects. */ 235 p_objects = (uint8_t *) 236 malloc(sizeof(cl_list_item_t) + (obj_size * obj_count)); 237 238 /* Make sure the allocation succeeded. */ 239 if (!p_objects) 240 return (CL_INSUFFICIENT_MEMORY); 241 else 242 memset(p_objects, 0, 243 sizeof(cl_list_item_t) + (obj_size * obj_count)); 244 245 /* Insert the allocation in our list. */ 246 cl_qlist_insert_tail(&p_pool->alloc_list, (cl_list_item_t *) p_objects); 247 p_objects += sizeof(cl_list_item_t); 248 249 /* initialize the new elements and add them to the free list */ 250 while (obj_count--) { 251 /* Setup the array of components for the current object. */ 252 p_pool->p_components[0] = p_objects; 253 for (i = 1; i < p_pool->num_components; i++) { 254 /* Calculate the pointer to the next component. */ 255 p_pool->p_components[i] = 256 (uint8_t *) p_pool->p_components[i - 1] + 257 p_pool->component_sizes[i - 1]; 258 } 259 260 /* 261 * call the user's initializer 262 * this can fail! 263 */ 264 if (p_pool->pfn_init) { 265 p_pool_item = NULL; 266 status = p_pool->pfn_init(p_pool->p_components, 267 p_pool->num_components, 268 (void *)p_pool->context, 269 &p_pool_item); 270 if (status != CL_SUCCESS) { 271 /* 272 * User initialization failed 273 * we may have only grown the pool by some partial amount 274 * Invoke the destructor for the object that failed 275 * initialization. 276 */ 277 if (p_pool->pfn_dtor) 278 p_pool->pfn_dtor(p_pool_item, 279 (void *)p_pool-> 280 context); 281 282 /* Return the user's status. */ 283 return (status); 284 } 285 CL_ASSERT(p_pool_item); 286 } else { 287 /* 288 * If no initializer is provided, assume that the pool item 289 * is stored at the beginning of the first component. 290 */ 291 p_pool_item = 292 (cl_pool_item_t *) p_pool->p_components[0]; 293 } 294 295#ifdef _DEBUG_ 296 /* 297 * Set the pool item's pool pointer to this pool so that we can 298 * check that items get returned to the correct pool. 299 */ 300 p_pool_item->p_pool = p_pool; 301#endif 302 303 /* Insert the new item in the free list, traping for failure. */ 304 cl_qlist_insert_head(&p_pool->free_list, 305 &p_pool_item->list_item); 306 307 p_pool->num_objects++; 308 309 /* move the pointer to the next item */ 310 p_objects += obj_size; 311 } 312 313 return (status); 314} 315 316cl_pool_item_t *cl_qcpool_get(IN cl_qcpool_t * const p_pool) 317{ 318 cl_list_item_t *p_list_item; 319 320 CL_ASSERT(p_pool); 321 CL_ASSERT(p_pool->state == CL_INITIALIZED); 322 323 if (cl_is_qlist_empty(&p_pool->free_list)) { 324 /* 325 * No object is available. 326 * Return NULL if the user does not want automatic growth. 327 */ 328 if (!p_pool->grow_size) 329 return (NULL); 330 331 /* We ran out of elements. Get more */ 332 cl_qcpool_grow(p_pool, p_pool->grow_size); 333 /* 334 * We may not have gotten everything we wanted but we might have 335 * gotten something. 336 */ 337 if (cl_is_qlist_empty(&p_pool->free_list)) 338 return (NULL); 339 } 340 341 p_list_item = cl_qlist_remove_head(&p_pool->free_list); 342 /* OK, at this point we have an object */ 343 CL_ASSERT(p_list_item != cl_qlist_end(&p_pool->free_list)); 344 return ((cl_pool_item_t *) p_list_item); 345} 346 347cl_pool_item_t *cl_qcpool_get_tail(IN cl_qcpool_t * const p_pool) 348{ 349 cl_list_item_t *p_list_item; 350 351 CL_ASSERT(p_pool); 352 CL_ASSERT(p_pool->state == CL_INITIALIZED); 353 354 if (cl_is_qlist_empty(&p_pool->free_list)) { 355 /* 356 * No object is available. 357 * Return NULL if the user does not want automatic growth. 358 */ 359 if (!p_pool->grow_size) 360 return (NULL); 361 362 /* We ran out of elements. Get more */ 363 cl_qcpool_grow(p_pool, p_pool->grow_size); 364 /* 365 * We may not have gotten everything we wanted but we might have 366 * gotten something. 367 */ 368 if (cl_is_qlist_empty(&p_pool->free_list)) 369 return (NULL); 370 } 371 372 p_list_item = cl_qlist_remove_tail(&p_pool->free_list); 373 /* OK, at this point we have an object */ 374 CL_ASSERT(p_list_item != cl_qlist_end(&p_pool->free_list)); 375 return ((cl_pool_item_t *) p_list_item); 376} 377 378/* 379 * IMPLEMENTATION OF QUICK GROW POOL 380 */ 381 382/* 383 * Callback to translate quick composite to quick grow pool 384 * initializer callback. 385 */ 386static cl_status_t 387__cl_qpool_init_cb(IN void **const p_comp_array, 388 IN const uint32_t num_components, 389 IN void *const context, 390 OUT cl_pool_item_t ** const pp_pool_item) 391{ 392 cl_qpool_t *p_pool = (cl_qpool_t *) context; 393 394 CL_ASSERT(p_pool); 395 CL_ASSERT(p_pool->pfn_init); 396 CL_ASSERT(num_components == 1); 397 398 UNUSED_PARAM(num_components); 399 400 return (p_pool->pfn_init(p_comp_array[0], (void *)p_pool->context, 401 pp_pool_item)); 402} 403 404/* 405 * Callback to translate quick composite to quick grow pool 406 * destructor callback. 407 */ 408static void 409__cl_qpool_dtor_cb(IN const cl_pool_item_t * const p_pool_item, 410 IN void *const context) 411{ 412 cl_qpool_t *p_pool = (cl_qpool_t *) context; 413 414 CL_ASSERT(p_pool); 415 CL_ASSERT(p_pool->pfn_dtor); 416 417 p_pool->pfn_dtor(p_pool_item, (void *)p_pool->context); 418} 419 420void cl_qpool_construct(IN cl_qpool_t * const p_pool) 421{ 422 memset(p_pool, 0, sizeof(cl_qpool_t)); 423 424 cl_qcpool_construct(&p_pool->qcpool); 425} 426 427cl_status_t 428cl_qpool_init(IN cl_qpool_t * const p_pool, 429 IN const size_t min_size, 430 IN const size_t max_size, 431 IN const size_t grow_size, 432 IN const size_t object_size, 433 IN cl_pfn_qpool_init_t pfn_initializer OPTIONAL, 434 IN cl_pfn_qpool_dtor_t pfn_destructor OPTIONAL, 435 IN const void *const context) 436{ 437 cl_status_t status; 438 439 CL_ASSERT(p_pool); 440 441 p_pool->pfn_init = pfn_initializer; /* may be NULL */ 442 p_pool->pfn_dtor = pfn_destructor; /* may be NULL */ 443 p_pool->context = context; 444 445 status = cl_qcpool_init(&p_pool->qcpool, min_size, max_size, grow_size, 446 &object_size, 1, 447 pfn_initializer ? __cl_qpool_init_cb : NULL, 448 pfn_destructor ? __cl_qpool_dtor_cb : NULL, 449 p_pool); 450 451 return (status); 452} 453 454/* 455 * IMPLEMENTATION OF COMPOSITE POOL 456 */ 457 458/* 459 * Callback to translate quick composite to compsite pool 460 * initializer callback. 461 */ 462static cl_status_t 463__cl_cpool_init_cb(IN void **const p_comp_array, 464 IN const uint32_t num_components, 465 IN void *const context, 466 OUT cl_pool_item_t ** const pp_pool_item) 467{ 468 cl_cpool_t *p_pool = (cl_cpool_t *) context; 469 cl_pool_obj_t *p_pool_obj; 470 cl_status_t status = CL_SUCCESS; 471 472 CL_ASSERT(p_pool); 473 474 /* 475 * Set our pointer to the list item, which is stored at the beginning of 476 * the first component. 477 */ 478 p_pool_obj = (cl_pool_obj_t *) p_comp_array[0]; 479 /* Set the pool item pointer for the caller. */ 480 *pp_pool_item = &p_pool_obj->pool_item; 481 482 /* Calculate the pointer to the user's first component. */ 483 p_comp_array[0] = ((uint8_t *) p_comp_array[0]) + sizeof(cl_pool_obj_t); 484 485 /* 486 * Set the object pointer in the pool object to point to the first of the 487 * user's components. 488 */ 489 p_pool_obj->p_object = p_comp_array[0]; 490 491 /* Invoke the user's constructor callback. */ 492 if (p_pool->pfn_init) { 493 status = p_pool->pfn_init(p_comp_array, num_components, 494 (void *)p_pool->context); 495 } 496 497 return (status); 498} 499 500/* 501 * Callback to translate quick composite to composite pool 502 * destructor callback. 503 */ 504static void 505__cl_cpool_dtor_cb(IN const cl_pool_item_t * const p_pool_item, 506 IN void *const context) 507{ 508 cl_cpool_t *p_pool = (cl_cpool_t *) context; 509 510 CL_ASSERT(p_pool); 511 CL_ASSERT(p_pool->pfn_dtor); 512 CL_ASSERT(((cl_pool_obj_t *) p_pool_item)->p_object); 513 514 /* Invoke the user's destructor callback. */ 515 p_pool->pfn_dtor((void *)((cl_pool_obj_t *) p_pool_item)->p_object, 516 (void *)p_pool->context); 517} 518 519void cl_cpool_construct(IN cl_cpool_t * const p_pool) 520{ 521 CL_ASSERT(p_pool); 522 523 memset(p_pool, 0, sizeof(cl_cpool_t)); 524 525 cl_qcpool_construct(&p_pool->qcpool); 526} 527 528cl_status_t 529cl_cpool_init(IN cl_cpool_t * const p_pool, 530 IN const size_t min_size, 531 IN const size_t max_size, 532 IN const size_t grow_size, 533 IN size_t * const component_sizes, 534 IN const uint32_t num_components, 535 IN cl_pfn_cpool_init_t pfn_initializer OPTIONAL, 536 IN cl_pfn_cpool_dtor_t pfn_destructor OPTIONAL, 537 IN const void *const context) 538{ 539 cl_status_t status; 540 541 CL_ASSERT(p_pool); 542 CL_ASSERT(num_components); 543 CL_ASSERT(component_sizes); 544 545 /* Add the size of the pool object to the first component. */ 546 component_sizes[0] += sizeof(cl_pool_obj_t); 547 548 /* Store callback function pointers. */ 549 p_pool->pfn_init = pfn_initializer; /* may be NULL */ 550 p_pool->pfn_dtor = pfn_destructor; /* may be NULL */ 551 p_pool->context = context; 552 553 status = cl_qcpool_init(&p_pool->qcpool, min_size, max_size, grow_size, 554 component_sizes, num_components, 555 __cl_cpool_init_cb, 556 pfn_destructor ? __cl_cpool_dtor_cb : NULL, 557 p_pool); 558 559 /* Restore the original value of the first component. */ 560 component_sizes[0] -= sizeof(cl_pool_obj_t); 561 562 return (status); 563} 564 565/* 566 * IMPLEMENTATION OF GROW POOL 567 */ 568 569/* 570 * Callback to translate quick composite to grow pool constructor callback. 571 */ 572static cl_status_t 573__cl_pool_init_cb(IN void **const pp_obj, 574 IN const uint32_t count, 575 IN void *const context, 576 OUT cl_pool_item_t ** const pp_pool_item) 577{ 578 cl_pool_t *p_pool = (cl_pool_t *) context; 579 cl_pool_obj_t *p_pool_obj; 580 cl_status_t status = CL_SUCCESS; 581 582 CL_ASSERT(p_pool); 583 CL_ASSERT(pp_obj); 584 CL_ASSERT(count == 1); 585 586 UNUSED_PARAM(count); 587 588 /* 589 * Set our pointer to the list item, which is stored at the beginning of 590 * the first component. 591 */ 592 p_pool_obj = (cl_pool_obj_t *) * pp_obj; 593 *pp_pool_item = &p_pool_obj->pool_item; 594 595 /* Calculate the pointer to the user's first component. */ 596 *pp_obj = ((uint8_t *) * pp_obj) + sizeof(cl_pool_obj_t); 597 598 /* 599 * Set the object pointer in the pool item to point to the first of the 600 * user's components. 601 */ 602 p_pool_obj->p_object = *pp_obj; 603 604 /* Invoke the user's constructor callback. */ 605 if (p_pool->pfn_init) 606 status = p_pool->pfn_init(*pp_obj, (void *)p_pool->context); 607 608 return (status); 609} 610 611/* 612 * Callback to translate quick composite to grow pool destructor callback. 613 */ 614static void 615__cl_pool_dtor_cb(IN const cl_pool_item_t * const p_pool_item, 616 IN void *const context) 617{ 618 cl_pool_t *p_pool = (cl_pool_t *) context; 619 620 CL_ASSERT(p_pool); 621 CL_ASSERT(p_pool->pfn_dtor); 622 CL_ASSERT(((cl_pool_obj_t *) p_pool_item)->p_object); 623 624 /* Invoke the user's destructor callback. */ 625 p_pool->pfn_dtor((void *)((cl_pool_obj_t *) p_pool_item)->p_object, 626 (void *)p_pool->context); 627} 628 629void cl_pool_construct(IN cl_pool_t * const p_pool) 630{ 631 CL_ASSERT(p_pool); 632 633 memset(p_pool, 0, sizeof(cl_pool_t)); 634 635 cl_qcpool_construct(&p_pool->qcpool); 636} 637 638cl_status_t 639cl_pool_init(IN cl_pool_t * const p_pool, 640 IN const size_t min_size, 641 IN const size_t max_size, 642 IN const size_t grow_size, 643 IN const size_t object_size, 644 IN cl_pfn_pool_init_t pfn_initializer OPTIONAL, 645 IN cl_pfn_pool_dtor_t pfn_destructor OPTIONAL, 646 IN const void *const context) 647{ 648 cl_status_t status; 649 size_t total_size; 650 651 CL_ASSERT(p_pool); 652 653 /* Add the size of the list item to the first component. */ 654 total_size = object_size + sizeof(cl_pool_obj_t); 655 656 /* Store callback function pointers. */ 657 p_pool->pfn_init = pfn_initializer; /* may be NULL */ 658 p_pool->pfn_dtor = pfn_destructor; /* may be NULL */ 659 p_pool->context = context; 660 661 /* 662 * We need an initializer in all cases for quick composite pool, since 663 * the user pointer must be manipulated to hide the prefixed cl_pool_obj_t. 664 */ 665 status = cl_qcpool_init(&p_pool->qcpool, min_size, max_size, grow_size, 666 &total_size, 1, __cl_pool_init_cb, 667 pfn_destructor ? __cl_pool_dtor_cb : NULL, 668 p_pool); 669 670 return (status); 671} 672