1/************************************************************************** 2 * 3 * Copyright 2006-2008 VMware, Inc., USA 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, FREE of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 18 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * The above copyright notice and this permission notice (including the 23 * next paragraph) shall be included in all copies or substantial portions 24 * of the Software. 25 * 26 * 27 **************************************************************************/ 28 29/** 30 * @file 31 * S-lab pool implementation. 32 * 33 * @sa http://en.wikipedia.org/wiki/Slab_allocation 34 * 35 * @author Thomas Hellstrom <thellstrom-at-vmware-dot-com> 36 * @author Jose Fonseca <jfonseca@vmware.com> 37 */ 38 39#include "pipe/p_compiler.h" 40#include "util/u_debug.h" 41#include "os/os_thread.h" 42#include "pipe/p_defines.h" 43#include "util/u_memory.h" 44#include "util/list.h" 45 46#include "pb_buffer.h" 47#include "pb_bufmgr.h" 48 49 50struct pb_slab; 51 52 53/** 54 * Buffer in a slab. 55 * 56 * Sub-allocation of a contiguous buffer. 57 */ 58struct pb_slab_buffer 59{ 60 struct pb_buffer base; 61 62 struct pb_slab *slab; 63 64 struct list_head head; 65 66 unsigned mapCount; 67 68 /** Offset relative to the start of the slab buffer. */ 69 pb_size start; 70 71 /** Use when validating, to signal that all mappings are finished */ 72 /* TODO: Actually validation does not reach this stage yet */ 73 cnd_t event; 74}; 75 76 77/** 78 * Slab -- a contiguous piece of memory. 79 */ 80struct pb_slab 81{ 82 struct list_head head; 83 struct list_head freeBuffers; 84 pb_size numBuffers; 85 pb_size numFree; 86 87 struct pb_slab_buffer *buffers; 88 struct pb_slab_manager *mgr; 89 90 /** Buffer from the provider */ 91 struct pb_buffer *bo; 92 93 void *virtual; 94}; 95 96 97/** 98 * It adds/removes slabs as needed in order to meet the allocation/destruction 99 * of individual buffers. 100 */ 101struct pb_slab_manager 102{ 103 struct pb_manager base; 104 105 /** From where we get our buffers */ 106 struct pb_manager *provider; 107 108 /** Size of the buffers we hand on downstream */ 109 pb_size bufSize; 110 111 /** Size of the buffers we request upstream */ 112 pb_size slabSize; 113 114 /** 115 * Alignment, usage to be used to allocate the slab buffers. 116 * 117 * We can only provide buffers which are consistent (in alignment, usage) 118 * with this description. 119 */ 120 struct pb_desc desc; 121 122 /** 123 * Partial slabs 124 * 125 * Full slabs are not stored in any list. Empty slabs are destroyed 126 * immediatly. 127 */ 128 struct list_head slabs; 129 130 mtx_t mutex; 131}; 132 133 134/** 135 * Wrapper around several slabs, therefore capable of handling buffers of 136 * multiple sizes. 137 * 138 * This buffer manager just dispatches buffer allocations to the appropriate slab 139 * manager, according to the requested buffer size, or by passes the slab 140 * managers altogether for even greater sizes. 141 * 142 * The data of this structure remains constant after 143 * initialization and thus needs no mutex protection. 144 */ 145struct pb_slab_range_manager 146{ 147 struct pb_manager base; 148 149 struct pb_manager *provider; 150 151 pb_size minBufSize; 152 pb_size maxBufSize; 153 154 /** @sa pb_slab_manager::desc */ 155 struct pb_desc desc; 156 157 unsigned numBuckets; 158 pb_size *bucketSizes; 159 160 /** Array of pb_slab_manager, one for each bucket size */ 161 struct pb_manager **buckets; 162}; 163 164 165static inline struct pb_slab_buffer * 166pb_slab_buffer(struct pb_buffer *buf) 167{ 168 assert(buf); 169 return (struct pb_slab_buffer *)buf; 170} 171 172 173static inline struct pb_slab_manager * 174pb_slab_manager(struct pb_manager *mgr) 175{ 176 assert(mgr); 177 return (struct pb_slab_manager *)mgr; 178} 179 180 181static inline struct pb_slab_range_manager * 182pb_slab_range_manager(struct pb_manager *mgr) 183{ 184 assert(mgr); 185 return (struct pb_slab_range_manager *)mgr; 186} 187 188 189/** 190 * Delete a buffer from the slab delayed list and put 191 * it on the slab FREE list. 192 */ 193static void 194pb_slab_buffer_destroy(struct pb_buffer *_buf) 195{ 196 struct pb_slab_buffer *buf = pb_slab_buffer(_buf); 197 struct pb_slab *slab = buf->slab; 198 struct pb_slab_manager *mgr = slab->mgr; 199 struct list_head *list = &buf->head; 200 201 mtx_lock(&mgr->mutex); 202 203 assert(!pipe_is_referenced(&buf->base.reference)); 204 205 buf->mapCount = 0; 206 207 LIST_DEL(list); 208 LIST_ADDTAIL(list, &slab->freeBuffers); 209 slab->numFree++; 210 211 if (slab->head.next == &slab->head) 212 LIST_ADDTAIL(&slab->head, &mgr->slabs); 213 214 /* If the slab becomes totally empty, free it */ 215 if (slab->numFree == slab->numBuffers) { 216 list = &slab->head; 217 LIST_DELINIT(list); 218 pb_reference(&slab->bo, NULL); 219 FREE(slab->buffers); 220 FREE(slab); 221 } 222 223 mtx_unlock(&mgr->mutex); 224} 225 226 227static void * 228pb_slab_buffer_map(struct pb_buffer *_buf, 229 enum pb_usage_flags flags, 230 void *flush_ctx) 231{ 232 struct pb_slab_buffer *buf = pb_slab_buffer(_buf); 233 234 /* XXX: it will be necessary to remap here to propagate flush_ctx */ 235 236 ++buf->mapCount; 237 return (void *) ((uint8_t *) buf->slab->virtual + buf->start); 238} 239 240 241static void 242pb_slab_buffer_unmap(struct pb_buffer *_buf) 243{ 244 struct pb_slab_buffer *buf = pb_slab_buffer(_buf); 245 246 --buf->mapCount; 247 if (buf->mapCount == 0) 248 cnd_broadcast(&buf->event); 249} 250 251 252static enum pipe_error 253pb_slab_buffer_validate(struct pb_buffer *_buf, 254 struct pb_validate *vl, 255 enum pb_usage_flags flags) 256{ 257 struct pb_slab_buffer *buf = pb_slab_buffer(_buf); 258 return pb_validate(buf->slab->bo, vl, flags); 259} 260 261 262static void 263pb_slab_buffer_fence(struct pb_buffer *_buf, 264 struct pipe_fence_handle *fence) 265{ 266 struct pb_slab_buffer *buf = pb_slab_buffer(_buf); 267 pb_fence(buf->slab->bo, fence); 268} 269 270 271static void 272pb_slab_buffer_get_base_buffer(struct pb_buffer *_buf, 273 struct pb_buffer **base_buf, 274 pb_size *offset) 275{ 276 struct pb_slab_buffer *buf = pb_slab_buffer(_buf); 277 pb_get_base_buffer(buf->slab->bo, base_buf, offset); 278 *offset += buf->start; 279} 280 281 282static const struct pb_vtbl 283pb_slab_buffer_vtbl = { 284 pb_slab_buffer_destroy, 285 pb_slab_buffer_map, 286 pb_slab_buffer_unmap, 287 pb_slab_buffer_validate, 288 pb_slab_buffer_fence, 289 pb_slab_buffer_get_base_buffer 290}; 291 292 293/** 294 * Create a new slab. 295 * 296 * Called when we ran out of free slabs. 297 */ 298static enum pipe_error 299pb_slab_create(struct pb_slab_manager *mgr) 300{ 301 struct pb_slab *slab; 302 struct pb_slab_buffer *buf; 303 unsigned numBuffers; 304 unsigned i; 305 enum pipe_error ret; 306 307 slab = CALLOC_STRUCT(pb_slab); 308 if (!slab) 309 return PIPE_ERROR_OUT_OF_MEMORY; 310 311 slab->bo = mgr->provider->create_buffer(mgr->provider, mgr->slabSize, &mgr->desc); 312 if(!slab->bo) { 313 ret = PIPE_ERROR_OUT_OF_MEMORY; 314 goto out_err0; 315 } 316 317 /* Note down the slab virtual address. All mappings are accessed directly 318 * through this address so it is required that the buffer is pinned. */ 319 slab->virtual = pb_map(slab->bo, 320 PB_USAGE_CPU_READ | 321 PB_USAGE_CPU_WRITE, NULL); 322 if(!slab->virtual) { 323 ret = PIPE_ERROR_OUT_OF_MEMORY; 324 goto out_err1; 325 } 326 pb_unmap(slab->bo); 327 328 numBuffers = slab->bo->size / mgr->bufSize; 329 330 slab->buffers = CALLOC(numBuffers, sizeof(*slab->buffers)); 331 if (!slab->buffers) { 332 ret = PIPE_ERROR_OUT_OF_MEMORY; 333 goto out_err1; 334 } 335 336 LIST_INITHEAD(&slab->head); 337 LIST_INITHEAD(&slab->freeBuffers); 338 slab->numBuffers = numBuffers; 339 slab->numFree = 0; 340 slab->mgr = mgr; 341 342 buf = slab->buffers; 343 for (i=0; i < numBuffers; ++i) { 344 pipe_reference_init(&buf->base.reference, 0); 345 buf->base.size = mgr->bufSize; 346 buf->base.alignment = 0; 347 buf->base.usage = 0; 348 buf->base.vtbl = &pb_slab_buffer_vtbl; 349 buf->slab = slab; 350 buf->start = i* mgr->bufSize; 351 buf->mapCount = 0; 352 cnd_init(&buf->event); 353 LIST_ADDTAIL(&buf->head, &slab->freeBuffers); 354 slab->numFree++; 355 buf++; 356 } 357 358 /* Add this slab to the list of partial slabs */ 359 LIST_ADDTAIL(&slab->head, &mgr->slabs); 360 361 return PIPE_OK; 362 363out_err1: 364 pb_reference(&slab->bo, NULL); 365out_err0: 366 FREE(slab); 367 return ret; 368} 369 370 371static struct pb_buffer * 372pb_slab_manager_create_buffer(struct pb_manager *_mgr, 373 pb_size size, 374 const struct pb_desc *desc) 375{ 376 struct pb_slab_manager *mgr = pb_slab_manager(_mgr); 377 static struct pb_slab_buffer *buf; 378 struct pb_slab *slab; 379 struct list_head *list; 380 381 /* check size */ 382 assert(size <= mgr->bufSize); 383 if(size > mgr->bufSize) 384 return NULL; 385 386 /* check if we can provide the requested alignment */ 387 assert(pb_check_alignment(desc->alignment, mgr->desc.alignment)); 388 if(!pb_check_alignment(desc->alignment, mgr->desc.alignment)) 389 return NULL; 390 assert(pb_check_alignment(desc->alignment, mgr->bufSize)); 391 if(!pb_check_alignment(desc->alignment, mgr->bufSize)) 392 return NULL; 393 394 assert(pb_check_usage(desc->usage, mgr->desc.usage)); 395 if(!pb_check_usage(desc->usage, mgr->desc.usage)) 396 return NULL; 397 398 mtx_lock(&mgr->mutex); 399 400 /* Create a new slab, if we run out of partial slabs */ 401 if (mgr->slabs.next == &mgr->slabs) { 402 (void) pb_slab_create(mgr); 403 if (mgr->slabs.next == &mgr->slabs) { 404 mtx_unlock(&mgr->mutex); 405 return NULL; 406 } 407 } 408 409 /* Allocate the buffer from a partial (or just created) slab */ 410 list = mgr->slabs.next; 411 slab = LIST_ENTRY(struct pb_slab, list, head); 412 413 /* If totally full remove from the partial slab list */ 414 if (--slab->numFree == 0) 415 LIST_DELINIT(list); 416 417 list = slab->freeBuffers.next; 418 LIST_DELINIT(list); 419 420 mtx_unlock(&mgr->mutex); 421 buf = LIST_ENTRY(struct pb_slab_buffer, list, head); 422 423 pipe_reference_init(&buf->base.reference, 1); 424 buf->base.alignment = desc->alignment; 425 buf->base.usage = desc->usage; 426 427 return &buf->base; 428} 429 430 431static void 432pb_slab_manager_flush(struct pb_manager *_mgr) 433{ 434 struct pb_slab_manager *mgr = pb_slab_manager(_mgr); 435 436 assert(mgr->provider->flush); 437 if(mgr->provider->flush) 438 mgr->provider->flush(mgr->provider); 439} 440 441 442static void 443pb_slab_manager_destroy(struct pb_manager *_mgr) 444{ 445 struct pb_slab_manager *mgr = pb_slab_manager(_mgr); 446 447 /* TODO: cleanup all allocated buffers */ 448 FREE(mgr); 449} 450 451 452struct pb_manager * 453pb_slab_manager_create(struct pb_manager *provider, 454 pb_size bufSize, 455 pb_size slabSize, 456 const struct pb_desc *desc) 457{ 458 struct pb_slab_manager *mgr; 459 460 mgr = CALLOC_STRUCT(pb_slab_manager); 461 if (!mgr) 462 return NULL; 463 464 mgr->base.destroy = pb_slab_manager_destroy; 465 mgr->base.create_buffer = pb_slab_manager_create_buffer; 466 mgr->base.flush = pb_slab_manager_flush; 467 468 mgr->provider = provider; 469 mgr->bufSize = bufSize; 470 mgr->slabSize = slabSize; 471 mgr->desc = *desc; 472 473 LIST_INITHEAD(&mgr->slabs); 474 475 (void) mtx_init(&mgr->mutex, mtx_plain); 476 477 return &mgr->base; 478} 479 480 481static struct pb_buffer * 482pb_slab_range_manager_create_buffer(struct pb_manager *_mgr, 483 pb_size size, 484 const struct pb_desc *desc) 485{ 486 struct pb_slab_range_manager *mgr = pb_slab_range_manager(_mgr); 487 pb_size bufSize; 488 pb_size reqSize = size; 489 enum pb_usage_flags i; 490 491 if(desc->alignment > reqSize) 492 reqSize = desc->alignment; 493 494 bufSize = mgr->minBufSize; 495 for (i = 0; i < mgr->numBuckets; ++i) { 496 if(bufSize >= reqSize) 497 return mgr->buckets[i]->create_buffer(mgr->buckets[i], size, desc); 498 bufSize *= 2; 499 } 500 501 /* Fall back to allocate a buffer object directly from the provider. */ 502 return mgr->provider->create_buffer(mgr->provider, size, desc); 503} 504 505 506static void 507pb_slab_range_manager_flush(struct pb_manager *_mgr) 508{ 509 struct pb_slab_range_manager *mgr = pb_slab_range_manager(_mgr); 510 511 /* Individual slabs don't hold any temporary buffers so no need to call them */ 512 513 assert(mgr->provider->flush); 514 if(mgr->provider->flush) 515 mgr->provider->flush(mgr->provider); 516} 517 518 519static void 520pb_slab_range_manager_destroy(struct pb_manager *_mgr) 521{ 522 struct pb_slab_range_manager *mgr = pb_slab_range_manager(_mgr); 523 unsigned i; 524 525 for (i = 0; i < mgr->numBuckets; ++i) 526 mgr->buckets[i]->destroy(mgr->buckets[i]); 527 FREE(mgr->buckets); 528 FREE(mgr->bucketSizes); 529 FREE(mgr); 530} 531 532 533struct pb_manager * 534pb_slab_range_manager_create(struct pb_manager *provider, 535 pb_size minBufSize, 536 pb_size maxBufSize, 537 pb_size slabSize, 538 const struct pb_desc *desc) 539{ 540 struct pb_slab_range_manager *mgr; 541 pb_size bufSize; 542 unsigned i; 543 544 if (!provider) 545 return NULL; 546 547 mgr = CALLOC_STRUCT(pb_slab_range_manager); 548 if (!mgr) 549 goto out_err0; 550 551 mgr->base.destroy = pb_slab_range_manager_destroy; 552 mgr->base.create_buffer = pb_slab_range_manager_create_buffer; 553 mgr->base.flush = pb_slab_range_manager_flush; 554 555 mgr->provider = provider; 556 mgr->minBufSize = minBufSize; 557 mgr->maxBufSize = maxBufSize; 558 559 mgr->numBuckets = 1; 560 bufSize = minBufSize; 561 while(bufSize < maxBufSize) { 562 bufSize *= 2; 563 ++mgr->numBuckets; 564 } 565 566 mgr->buckets = CALLOC(mgr->numBuckets, sizeof(*mgr->buckets)); 567 if (!mgr->buckets) 568 goto out_err1; 569 570 bufSize = minBufSize; 571 for (i = 0; i < mgr->numBuckets; ++i) { 572 mgr->buckets[i] = pb_slab_manager_create(provider, bufSize, slabSize, desc); 573 if(!mgr->buckets[i]) 574 goto out_err2; 575 bufSize *= 2; 576 } 577 578 return &mgr->base; 579 580out_err2: 581 for (i = 0; i < mgr->numBuckets; ++i) 582 if(mgr->buckets[i]) 583 mgr->buckets[i]->destroy(mgr->buckets[i]); 584 FREE(mgr->buckets); 585out_err1: 586 FREE(mgr); 587out_err0: 588 return NULL; 589} 590