pb_bufmgr_slab.c revision 0bb852fa49e7f9a31036089ea4f5dfbd312a4a3a
1/************************************************************************** 2 * 3 * Copyright 2006-2008 Tungsten Graphics, Inc., Cedar Park, TX., 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 * @author Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> 34 * @author Jose Fonseca <jrfonseca@tungstengraphics.com> 35 */ 36 37#include "pipe/p_compiler.h" 38#include "pipe/p_error.h" 39#include "pipe/p_debug.h" 40#include "pipe/p_thread.h" 41#include "pipe/p_defines.h" 42#include "util/u_memory.h" 43#include "util/u_double_list.h" 44#include "util/u_time.h" 45 46#include "pb_buffer.h" 47#include "pb_bufmgr.h" 48 49 50struct pb_slab; 51 52struct pb_slab_buffer 53{ 54 struct pb_buffer base; 55 56 struct pb_slab *slab; 57 struct list_head head; 58 unsigned mapCount; 59 size_t start; 60 pipe_condvar event; 61}; 62 63struct pb_slab 64{ 65 struct list_head head; 66 struct list_head freeBuffers; 67 size_t numBuffers; 68 size_t numFree; 69 struct pb_slab_buffer *buffers; 70 struct pb_slab_manager *mgr; 71 72 struct pb_buffer *bo; 73 void *virtual; 74}; 75 76struct pb_slab_manager 77{ 78 struct pb_manager base; 79 80 struct pb_manager *provider; 81 size_t bufSize; 82 size_t slabSize; 83 struct pb_desc desc; 84 85 struct list_head slabs; 86 struct list_head freeSlabs; 87 88 pipe_mutex mutex; 89}; 90 91/** 92 * The data of this structure remains constant after 93 * initialization and thus needs no mutex protection. 94 */ 95struct pb_slab_range_manager 96{ 97 struct pb_manager base; 98 99 struct pb_manager *provider; 100 size_t minBufSize; 101 size_t maxBufSize; 102 struct pb_desc desc; 103 104 unsigned numBuckets; 105 size_t *bucketSizes; 106 struct pb_manager **buckets; 107}; 108 109 110static INLINE struct pb_slab_buffer * 111pb_slab_buffer(struct pb_buffer *buf) 112{ 113 assert(buf); 114 return (struct pb_slab_buffer *)buf; 115} 116 117 118static INLINE struct pb_slab_manager * 119pb_slab_manager(struct pb_manager *mgr) 120{ 121 assert(mgr); 122 return (struct pb_slab_manager *)mgr; 123} 124 125 126static INLINE struct pb_slab_range_manager * 127pb_slab_range_manager(struct pb_manager *mgr) 128{ 129 assert(mgr); 130 return (struct pb_slab_range_manager *)mgr; 131} 132 133 134/** 135 * Delete a buffer from the slab delayed list and put 136 * it on the slab FREE list. 137 */ 138static void 139pb_slab_buffer_destroy(struct pb_buffer *_buf) 140{ 141 struct pb_slab_buffer *buf = pb_slab_buffer(_buf); 142 struct pb_slab *slab = buf->slab; 143 struct pb_slab_manager *mgr = slab->mgr; 144 struct list_head *list = &buf->head; 145 146 pipe_mutex_lock(mgr->mutex); 147 148 assert(buf->base.base.refcount == 0); 149 150 buf->mapCount = 0; 151 152 LIST_DEL(list); 153 LIST_ADDTAIL(list, &slab->freeBuffers); 154 slab->numFree++; 155 156 if (slab->head.next == &slab->head) 157 LIST_ADDTAIL(&slab->head, &mgr->slabs); 158 159 if (slab->numFree == slab->numBuffers) { 160 list = &slab->head; 161 LIST_DEL(list); 162 LIST_ADDTAIL(list, &mgr->freeSlabs); 163 } 164 165 if (mgr->slabs.next == &mgr->slabs || slab->numFree 166 != slab->numBuffers) { 167 168 struct list_head *next; 169 170 for (list = mgr->freeSlabs.next, next = list->next; list 171 != &mgr->freeSlabs; list = next, next = list->next) { 172 173 slab = LIST_ENTRY(struct pb_slab, list, head); 174 175 LIST_DELINIT(list); 176 pb_reference(&slab->bo, NULL); 177 FREE(slab->buffers); 178 FREE(slab); 179 } 180 } 181 182 pipe_mutex_unlock(mgr->mutex); 183} 184 185 186static void * 187pb_slab_buffer_map(struct pb_buffer *_buf, 188 unsigned flags) 189{ 190 struct pb_slab_buffer *buf = pb_slab_buffer(_buf); 191 192 ++buf->mapCount; 193 return (void *) ((uint8_t *) buf->slab->virtual + buf->start); 194} 195 196 197static void 198pb_slab_buffer_unmap(struct pb_buffer *_buf) 199{ 200 struct pb_slab_buffer *buf = pb_slab_buffer(_buf); 201 202 --buf->mapCount; 203 if (buf->mapCount == 0) 204 pipe_condvar_broadcast(buf->event); 205} 206 207 208static void 209pb_slab_buffer_get_base_buffer(struct pb_buffer *_buf, 210 struct pb_buffer **base_buf, 211 unsigned *offset) 212{ 213 struct pb_slab_buffer *buf = pb_slab_buffer(_buf); 214 pb_get_base_buffer(buf->slab->bo, base_buf, offset); 215 *offset += buf->start; 216} 217 218 219static const struct pb_vtbl 220pb_slab_buffer_vtbl = { 221 pb_slab_buffer_destroy, 222 pb_slab_buffer_map, 223 pb_slab_buffer_unmap, 224 pb_slab_buffer_get_base_buffer 225}; 226 227 228static enum pipe_error 229pb_slab_create(struct pb_slab_manager *mgr) 230{ 231 struct pb_slab *slab; 232 struct pb_slab_buffer *buf; 233 unsigned numBuffers; 234 unsigned i; 235 enum pipe_error ret; 236 237 slab = CALLOC_STRUCT(pb_slab); 238 if (!slab) 239 return PIPE_ERROR_OUT_OF_MEMORY; 240 241 /* 242 * FIXME: We should perhaps allow some variation in slabsize in order 243 * to efficiently reuse slabs. 244 */ 245 246 slab->bo = mgr->provider->create_buffer(mgr->provider, mgr->slabSize, &mgr->desc); 247 if(!slab->bo) { 248 ret = PIPE_ERROR_OUT_OF_MEMORY; 249 goto out_err0; 250 } 251 252 slab->virtual = pb_map(slab->bo, 253 PIPE_BUFFER_USAGE_CPU_READ | 254 PIPE_BUFFER_USAGE_CPU_WRITE); 255 if(!slab->virtual) { 256 ret = PIPE_ERROR_OUT_OF_MEMORY; 257 goto out_err1; 258 } 259 260 pb_unmap(slab->bo); 261 262 numBuffers = slab->bo->base.size / mgr->bufSize; 263 264 slab->buffers = CALLOC(numBuffers, sizeof(*slab->buffers)); 265 if (!slab->buffers) { 266 ret = PIPE_ERROR_OUT_OF_MEMORY; 267 goto out_err1; 268 } 269 270 LIST_INITHEAD(&slab->head); 271 LIST_INITHEAD(&slab->freeBuffers); 272 slab->numBuffers = numBuffers; 273 slab->numFree = 0; 274 slab->mgr = mgr; 275 276 buf = slab->buffers; 277 for (i=0; i < numBuffers; ++i) { 278 buf->base.base.refcount = 0; 279 buf->base.base.size = mgr->bufSize; 280 buf->base.base.alignment = 0; 281 buf->base.base.usage = 0; 282 buf->base.vtbl = &pb_slab_buffer_vtbl; 283 buf->slab = slab; 284 buf->start = i* mgr->bufSize; 285 buf->mapCount = 0; 286 pipe_condvar_init(buf->event); 287 LIST_ADDTAIL(&buf->head, &slab->freeBuffers); 288 slab->numFree++; 289 buf++; 290 } 291 292 LIST_ADDTAIL(&slab->head, &mgr->slabs); 293 294 return PIPE_OK; 295 296out_err1: 297 pb_reference(&slab->bo, NULL); 298out_err0: 299 FREE(slab); 300 return ret; 301} 302 303 304static struct pb_buffer * 305pb_slab_manager_create_buffer(struct pb_manager *_mgr, 306 size_t size, 307 const struct pb_desc *desc) 308{ 309 struct pb_slab_manager *mgr = pb_slab_manager(_mgr); 310 static struct pb_slab_buffer *buf; 311 struct pb_slab *slab; 312 struct list_head *list; 313 314 /* check size */ 315 assert(size <= mgr->bufSize); 316 if(size > mgr->bufSize) 317 return NULL; 318 319 /* check if we can provide the requested alignment */ 320 assert(pb_check_alignment(desc->alignment, mgr->desc.alignment)); 321 if(!pb_check_alignment(desc->alignment, mgr->desc.alignment)) 322 return NULL; 323 assert(pb_check_alignment(desc->alignment, mgr->bufSize)); 324 if(!pb_check_alignment(desc->alignment, mgr->bufSize)) 325 return NULL; 326 327 assert(pb_check_usage(desc->usage, mgr->desc.usage)); 328 if(!pb_check_usage(desc->usage, mgr->desc.usage)) 329 return NULL; 330 331 pipe_mutex_lock(mgr->mutex); 332 if (mgr->slabs.next == &mgr->slabs) { 333 (void) pb_slab_create(mgr); 334 if (mgr->slabs.next == &mgr->slabs) { 335 pipe_mutex_unlock(mgr->mutex); 336 return NULL; 337 } 338 } 339 list = mgr->slabs.next; 340 slab = LIST_ENTRY(struct pb_slab, list, head); 341 if (--slab->numFree == 0) 342 LIST_DELINIT(list); 343 344 list = slab->freeBuffers.next; 345 LIST_DELINIT(list); 346 347 pipe_mutex_unlock(mgr->mutex); 348 buf = LIST_ENTRY(struct pb_slab_buffer, list, head); 349 350 ++buf->base.base.refcount; 351 buf->base.base.alignment = desc->alignment; 352 buf->base.base.usage = desc->usage; 353 354 return &buf->base; 355} 356 357 358static void 359pb_slab_manager_destroy(struct pb_manager *_mgr) 360{ 361 struct pb_slab_manager *mgr = pb_slab_manager(_mgr); 362 363 /* TODO: cleanup all allocated buffers */ 364 FREE(mgr); 365} 366 367 368struct pb_manager * 369pb_slab_manager_create(struct pb_manager *provider, 370 size_t bufSize, 371 size_t slabSize, 372 const struct pb_desc *desc) 373{ 374 struct pb_slab_manager *mgr; 375 376 mgr = CALLOC_STRUCT(pb_slab_manager); 377 if (!mgr) 378 return NULL; 379 380 mgr->base.destroy = pb_slab_manager_destroy; 381 mgr->base.create_buffer = pb_slab_manager_create_buffer; 382 383 mgr->provider = provider; 384 mgr->bufSize = bufSize; 385 mgr->slabSize = slabSize; 386 mgr->desc = *desc; 387 388 LIST_INITHEAD(&mgr->slabs); 389 LIST_INITHEAD(&mgr->freeSlabs); 390 391 pipe_mutex_init(mgr->mutex); 392 393 return &mgr->base; 394} 395 396 397static struct pb_buffer * 398pb_slab_range_manager_create_buffer(struct pb_manager *_mgr, 399 size_t size, 400 const struct pb_desc *desc) 401{ 402 struct pb_slab_range_manager *mgr = pb_slab_range_manager(_mgr); 403 size_t bufSize; 404 unsigned i; 405 406 bufSize = mgr->minBufSize; 407 for (i = 0; i < mgr->numBuckets; ++i) { 408 if(bufSize >= size) 409 return mgr->buckets[i]->create_buffer(mgr->buckets[i], size, desc); 410 bufSize *= 2; 411 } 412 413 /* Fall back to allocate a buffer object directly from the provider. */ 414 return mgr->provider->create_buffer(mgr->provider, size, desc); 415} 416 417 418static void 419pb_slab_range_manager_destroy(struct pb_manager *_mgr) 420{ 421 struct pb_slab_range_manager *mgr = pb_slab_range_manager(_mgr); 422 unsigned i; 423 424 for (i = 0; i < mgr->numBuckets; ++i) 425 mgr->buckets[i]->destroy(mgr->buckets[i]); 426 FREE(mgr->buckets); 427 FREE(mgr->bucketSizes); 428 FREE(mgr); 429} 430 431 432struct pb_manager * 433pb_slab_range_manager_create(struct pb_manager *provider, 434 size_t minBufSize, 435 size_t maxBufSize, 436 size_t slabSize, 437 const struct pb_desc *desc) 438{ 439 struct pb_slab_range_manager *mgr; 440 size_t bufSize; 441 unsigned i; 442 443 if(!provider) 444 return NULL; 445 446 mgr = CALLOC_STRUCT(pb_slab_range_manager); 447 if (!mgr) 448 goto out_err0; 449 450 mgr->base.destroy = pb_slab_range_manager_destroy; 451 mgr->base.create_buffer = pb_slab_range_manager_create_buffer; 452 453 mgr->provider = provider; 454 mgr->minBufSize = minBufSize; 455 mgr->maxBufSize = maxBufSize; 456 457 mgr->numBuckets = 1; 458 bufSize = minBufSize; 459 while(bufSize < maxBufSize) { 460 bufSize *= 2; 461 ++mgr->numBuckets; 462 } 463 464 mgr->buckets = CALLOC(mgr->numBuckets, sizeof(*mgr->buckets)); 465 if (!mgr->buckets) 466 goto out_err1; 467 468 bufSize = minBufSize; 469 for (i = 0; i < mgr->numBuckets; ++i) { 470 mgr->buckets[i] = pb_slab_manager_create(provider, bufSize, slabSize, desc); 471 if(!mgr->buckets[i]) 472 goto out_err2; 473 bufSize *= 2; 474 } 475 476 return &mgr->base; 477 478out_err2: 479 for (i = 0; i < mgr->numBuckets; ++i) 480 if(mgr->buckets[i]) 481 mgr->buckets[i]->destroy(mgr->buckets[i]); 482 FREE(mgr->buckets); 483out_err1: 484 FREE(mgr); 485out_err0: 486 return NULL; 487} 488