Home | History | Annotate | Download | only in src
      1 /**************************************************************************
      2  *
      3  * Copyright 2006-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA
      4  * All Rights Reserved.
      5  * Copyright 2009 VMware, Inc., Palo Alto, CA., USA
      6  * All Rights Reserved.
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining a
      9  * copy of this software and associated documentation files (the
     10  * "Software"), to deal in the Software without restriction, including
     11  * without limitation the rights to use, copy, modify, merge, publish,
     12  * distribute, sub license, and/or sell copies of the Software, and to
     13  * permit persons to whom the Software is furnished to do so, subject to
     14  * the following conditions:
     15  *
     16  * The above copyright notice and this permission notice (including the
     17  * next paragraph) shall be included in all copies or substantial portions
     18  * of the Software.
     19  *
     20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     22  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
     23  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
     24  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
     25  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
     26  * USE OR OTHER DEALINGS IN THE SOFTWARE.
     27  *
     28  **************************************************************************/
     29 /*
     30  * Authors: Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
     31  */
     32 
     33 #ifdef HAVE_CONFIG_H
     34 #include "config.h"
     35 #endif
     36 
     37 #include <drm/psb_ttm_placement_user.h>
     38 #include <stdint.h>
     39 #include <sys/time.h>
     40 #include <errno.h>
     41 #include <unistd.h>
     42 #include <assert.h>
     43 #include <string.h>
     44 #include <sys/mman.h>
     45 #include <xf86drm.h>
     46 #include "wsbm_pool.h"
     47 #include "wsbm_fencemgr.h"
     48 #include "wsbm_priv.h"
     49 #include "wsbm_manager.h"
     50 
     51 #define WSBM_SLABPOOL_ALLOC_RETRIES 100
     52 #define DRMRESTARTCOMMANDWRITE(_fd, _val, _arg, _ret)			\
     53 	do {								\
     54 		(_ret) = drmCommandWrite(_fd, _val, &(_arg), sizeof(_arg)); \
     55 	} while ((_ret) == -EAGAIN || (_ret) == -ERESTART);		\
     56 
     57 #define DRMRESTARTCOMMANDWRITEREAD(_fd, _val, _arg, _ret)		\
     58 	do {								\
     59 		(_ret) = drmCommandWriteRead(_fd, _val, &(_arg), sizeof(_arg)); \
     60 	} while ((_ret) == -EAGAIN || (_ret) == -ERESTART);		\
     61 
     62 
     63 #ifdef DEBUG_FENCESIGNALED
     64 static int createbuffer = 0;
     65 static int fencesignaled = 0;
     66 #endif
     67 
     68 struct _WsbmSlab;
     69 
     70 struct _WsbmSlabBuffer
     71 {
     72     struct _WsbmKernelBuf kBuf;
     73     struct _WsbmBufStorage storage;
     74     struct _WsbmCond event;
     75 
     76     /*
     77      * Remains constant after creation.
     78      */
     79 
     80     int isSlabBuffer;
     81     struct _WsbmSlab *parent;
     82     uint32_t start;
     83     void *virtual;
     84     unsigned long requestedSize;
     85     uint64_t mapHandle;
     86 
     87     /*
     88      * Protected by struct _WsbmSlabSizeHeader::mutex
     89      */
     90 
     91     struct _WsbmListHead head;
     92 
     93     /*
     94      * Protected by this::mutex
     95      */
     96 
     97     struct _WsbmFenceObject *fence;
     98     uint32_t fenceType;
     99     struct _WsbmAtomic writers;	       /* (Only upping) */
    100     int unFenced;
    101 };
    102 
    103 struct _WsbmSlabPool;
    104 struct _WsbmSlabKernelBO
    105 {
    106 
    107     /*
    108      * Constant at creation
    109      */
    110 
    111     struct _WsbmKernelBuf kBuf;
    112     uint32_t pageAlignment;
    113     void *virtual;
    114     unsigned long actualSize;
    115     uint64_t mapHandle;
    116 
    117     /*
    118      * Protected by struct _WsbmSlabCache::mutex
    119      */
    120 
    121     struct _WsbmSlabPool *slabPool;
    122     uint32_t proposedPlacement;
    123     struct _WsbmListHead timeoutHead;
    124     struct _WsbmListHead head;
    125     struct timeval timeFreed;
    126 };
    127 
    128 struct _WsbmSlab
    129 {
    130     struct _WsbmListHead head;
    131     struct _WsbmListHead freeBuffers;
    132     uint32_t numBuffers;
    133     uint32_t numFree;
    134     struct _WsbmSlabBuffer *buffers;
    135     struct _WsbmSlabSizeHeader *header;
    136     struct _WsbmSlabKernelBO *kbo;
    137 };
    138 
    139 struct _WsbmSlabSizeHeader
    140 {
    141     /*
    142      * Constant at creation.
    143      */
    144     struct _WsbmSlabPool *slabPool;
    145     uint32_t bufSize;
    146 
    147     /*
    148      * Protected by this::mutex
    149      */
    150 
    151     struct _WsbmListHead slabs;
    152     struct _WsbmListHead freeSlabs;
    153     struct _WsbmListHead delayedBuffers;
    154     uint32_t numDelayed;
    155     struct _WsbmMutex mutex;
    156 };
    157 
    158 struct _WsbmSlabCache
    159 {
    160     struct timeval slabTimeout;
    161     struct timeval checkInterval;
    162     struct timeval nextCheck;
    163     struct _WsbmListHead timeoutList;
    164     struct _WsbmListHead unCached;
    165     struct _WsbmListHead cached;
    166     struct _WsbmMutex mutex;
    167 };
    168 
    169 struct _WsbmSlabPool
    170 {
    171     struct _WsbmBufferPool pool;
    172 
    173     /*
    174      * The data of this structure remains constant after
    175      * initialization and thus needs no mutex protection.
    176      */
    177 
    178     unsigned int devOffset;
    179     struct _WsbmSlabCache *cache;
    180     uint32_t proposedPlacement;
    181     uint32_t validMask;
    182     uint32_t *bucketSizes;
    183     uint32_t numBuckets;
    184     uint32_t pageSize;
    185     int pageAlignment;
    186     int maxSlabSize;
    187     int desiredNumBuffers;
    188     struct _WsbmSlabSizeHeader *headers;
    189 };
    190 
    191 static inline struct _WsbmSlabPool *
    192 slabPoolFromPool(struct _WsbmBufferPool *pool)
    193 {
    194     return containerOf(pool, struct _WsbmSlabPool, pool);
    195 }
    196 
    197 static inline struct _WsbmSlabPool *
    198 slabPoolFromBuf(struct _WsbmSlabBuffer *sBuf)
    199 {
    200     return slabPoolFromPool(sBuf->storage.pool);
    201 }
    202 
    203 static inline struct _WsbmSlabBuffer *
    204 slabBuffer(struct _WsbmBufStorage *buf)
    205 {
    206     return containerOf(buf, struct _WsbmSlabBuffer, storage);
    207 }
    208 
    209 /*
    210  * FIXME: Perhaps arrange timeout slabs in size buckets for fast
    211  * retreival??
    212  */
    213 
    214 static inline int
    215 wsbmTimeAfterEq(struct timeval *arg1, struct timeval *arg2)
    216 {
    217     return ((arg1->tv_sec > arg2->tv_sec) ||
    218 	    ((arg1->tv_sec == arg2->tv_sec) &&
    219 	     (arg1->tv_usec > arg2->tv_usec)));
    220 }
    221 
    222 static inline void
    223 wsbmTimeAdd(struct timeval *arg, struct timeval *add)
    224 {
    225     unsigned int sec;
    226 
    227     arg->tv_sec += add->tv_sec;
    228     arg->tv_usec += add->tv_usec;
    229     sec = arg->tv_usec / 1000000;
    230     arg->tv_sec += sec;
    231     arg->tv_usec -= sec * 1000000;
    232 }
    233 
    234 static void
    235 wsbmFreeKernelBO(struct _WsbmSlabKernelBO *kbo)
    236 {
    237     struct ttm_pl_reference_req arg;
    238     struct _WsbmSlabPool *slabPool;
    239 
    240     if (!kbo)
    241 	return;
    242 
    243     slabPool = kbo->slabPool;
    244     arg.handle = kbo->kBuf.handle;
    245     (void)munmap(kbo->virtual, kbo->actualSize);
    246     (void)drmCommandWrite(slabPool->pool.fd,
    247 			  slabPool->devOffset + TTM_PL_UNREF, &arg,
    248 			  sizeof(arg));
    249     free(kbo);
    250 }
    251 
    252 static void
    253 wsbmFreeTimeoutKBOsLocked(struct _WsbmSlabCache *cache, struct timeval *time)
    254 {
    255     struct _WsbmListHead *list, *next;
    256     struct _WsbmSlabKernelBO *kbo;
    257 
    258     if (!wsbmTimeAfterEq(time, &cache->nextCheck))
    259 	return;
    260 
    261     WSBMLISTFOREACHSAFE(list, next, &cache->timeoutList) {
    262 	kbo = WSBMLISTENTRY(list, struct _WsbmSlabKernelBO, timeoutHead);
    263 
    264 	if (!wsbmTimeAfterEq(time, &kbo->timeFreed))
    265 	    break;
    266 
    267 	WSBMLISTDELINIT(&kbo->timeoutHead);
    268 	WSBMLISTDELINIT(&kbo->head);
    269 	wsbmFreeKernelBO(kbo);
    270     }
    271 
    272     cache->nextCheck = *time;
    273     wsbmTimeAdd(&cache->nextCheck, &cache->checkInterval);
    274 }
    275 
    276 /*
    277  * Add a _SlabKernelBO to the free slab manager.
    278  * This means that it is available for reuse, but if it's not
    279  * reused in a while, it will be freed.
    280  */
    281 
    282 static void
    283 wsbmSetKernelBOFree(struct _WsbmSlabCache *cache,
    284 		    struct _WsbmSlabKernelBO *kbo)
    285 {
    286     struct timeval time;
    287     struct timeval timeFreed;
    288 
    289     gettimeofday(&time, NULL);
    290     timeFreed = time;
    291     WSBM_MUTEX_LOCK(&cache->mutex);
    292     wsbmTimeAdd(&timeFreed, &cache->slabTimeout);
    293     kbo->timeFreed = timeFreed;
    294 
    295     if (kbo->kBuf.placement & TTM_PL_FLAG_CACHED)
    296 	WSBMLISTADD(&kbo->head, &cache->cached);
    297     else
    298 	WSBMLISTADD(&kbo->head, &cache->unCached);
    299 
    300     WSBMLISTADDTAIL(&kbo->timeoutHead, &cache->timeoutList);
    301     wsbmFreeTimeoutKBOsLocked(cache, &time);
    302 
    303     WSBM_MUTEX_UNLOCK(&cache->mutex);
    304 }
    305 
    306 /*
    307  * Get a _SlabKernelBO for us to use as storage for a slab.
    308  */
    309 
    310 static struct _WsbmSlabKernelBO *
    311 wsbmAllocKernelBO(struct _WsbmSlabSizeHeader *header)
    312 {
    313     struct _WsbmSlabPool *slabPool = header->slabPool;
    314     struct _WsbmSlabCache *cache = slabPool->cache;
    315     struct _WsbmListHead *list, *head;
    316     uint32_t size = header->bufSize * slabPool->desiredNumBuffers;
    317     struct _WsbmSlabKernelBO *kbo;
    318     struct _WsbmSlabKernelBO *kboTmp;
    319     int ret;
    320 
    321     /*
    322      * FIXME: We should perhaps allow some variation in slabsize in order
    323      * to efficiently reuse slabs.
    324      */
    325 
    326     size = (size <= (uint32_t) slabPool->maxSlabSize) ? size : (uint32_t) slabPool->maxSlabSize;
    327     if (size < header->bufSize)
    328 	size = header->bufSize;
    329     size = (size + slabPool->pageSize - 1) & ~(slabPool->pageSize - 1);
    330     WSBM_MUTEX_LOCK(&cache->mutex);
    331 
    332     kbo = NULL;
    333 
    334   retry:
    335     head = (slabPool->proposedPlacement & TTM_PL_FLAG_CACHED) ?
    336 	&cache->cached : &cache->unCached;
    337 
    338     WSBMLISTFOREACH(list, head) {
    339 	kboTmp = WSBMLISTENTRY(list, struct _WsbmSlabKernelBO, head);
    340 
    341 	if ((kboTmp->actualSize == size) &&
    342 	    (slabPool->pageAlignment == 0 ||
    343 	     (kboTmp->pageAlignment % slabPool->pageAlignment) == 0)) {
    344 
    345 	    if (!kbo)
    346 		kbo = kboTmp;
    347 
    348 	    if ((kbo->proposedPlacement ^ slabPool->proposedPlacement) == 0)
    349 		break;
    350 
    351 	}
    352     }
    353 
    354     if (kbo) {
    355 	WSBMLISTDELINIT(&kbo->head);
    356 	WSBMLISTDELINIT(&kbo->timeoutHead);
    357     }
    358 
    359     WSBM_MUTEX_UNLOCK(&cache->mutex);
    360 
    361     if (kbo) {
    362 	uint32_t new_mask =
    363 	    kbo->proposedPlacement ^ slabPool->proposedPlacement;
    364 
    365 	ret = 0;
    366 	if (new_mask) {
    367 	    union ttm_pl_setstatus_arg arg;
    368 	    struct ttm_pl_setstatus_req *req = &arg.req;
    369 	    struct ttm_pl_rep *rep = &arg.rep;
    370 
    371 	    req->handle = kbo->kBuf.handle;
    372 	    req->set_placement = slabPool->proposedPlacement & new_mask;
    373 	    req->clr_placement = ~slabPool->proposedPlacement & new_mask;
    374 	    DRMRESTARTCOMMANDWRITEREAD(slabPool->pool.fd,
    375 				       slabPool->devOffset + TTM_PL_SETSTATUS,
    376 				       arg, ret);
    377 	    if (ret == 0) {
    378 		kbo->kBuf.gpuOffset = rep->gpu_offset;
    379 		kbo->kBuf.placement = rep->placement;
    380 	    }
    381 	    kbo->proposedPlacement = slabPool->proposedPlacement;
    382 	}
    383 
    384 	if (ret == 0)
    385 	    return kbo;
    386 
    387 	wsbmFreeKernelBO(kbo);
    388 	kbo = NULL;
    389 	goto retry;
    390     }
    391 
    392     kbo = calloc(1, sizeof(*kbo));
    393     if (!kbo)
    394 	return NULL;
    395 
    396     {
    397 	union ttm_pl_create_arg arg;
    398 
    399 	kbo->slabPool = slabPool;
    400 	WSBMINITLISTHEAD(&kbo->head);
    401 	WSBMINITLISTHEAD(&kbo->timeoutHead);
    402 
    403 	arg.req.size = size;
    404 	arg.req.placement = slabPool->proposedPlacement;
    405 	arg.req.page_alignment = slabPool->pageAlignment;
    406 
    407 	DRMRESTARTCOMMANDWRITEREAD(slabPool->pool.fd,
    408 				   slabPool->devOffset + TTM_PL_CREATE,
    409 				   arg, ret);
    410 	if (ret)
    411 	    goto out_err0;
    412 
    413 	kbo->kBuf.gpuOffset = arg.rep.gpu_offset;
    414 	kbo->kBuf.placement = arg.rep.placement;
    415 	kbo->kBuf.handle = arg.rep.handle;
    416 
    417 	kbo->actualSize = arg.rep.bo_size;
    418 	kbo->mapHandle = arg.rep.map_handle;
    419 	kbo->proposedPlacement = slabPool->proposedPlacement;
    420     }
    421 
    422     kbo->virtual = mmap(0, kbo->actualSize,
    423 			PROT_READ | PROT_WRITE, MAP_SHARED,
    424 			slabPool->pool.fd, kbo->mapHandle);
    425 
    426     if (kbo->virtual == MAP_FAILED) {
    427 	ret = -errno;
    428 	goto out_err1;
    429     }
    430 
    431     return kbo;
    432 
    433   out_err1:
    434     {
    435 	struct ttm_pl_reference_req arg = {.handle = kbo->kBuf.handle };
    436 
    437 	(void)drmCommandWrite(slabPool->pool.fd,
    438 			      slabPool->devOffset + TTM_PL_UNREF,
    439 			      &arg, sizeof(arg));
    440     }
    441   out_err0:
    442     free(kbo);
    443     return NULL;
    444 }
    445 
    446 static int
    447 wsbmAllocSlab(struct _WsbmSlabSizeHeader *header)
    448 {
    449     struct _WsbmSlab *slab;
    450     struct _WsbmSlabBuffer *sBuf;
    451     uint32_t numBuffers;
    452     uint32_t ret;
    453     uint32_t i;
    454 
    455     slab = calloc(1, sizeof(*slab));
    456     if (!slab)
    457 	return -ENOMEM;
    458 
    459     slab->kbo = wsbmAllocKernelBO(header);
    460     if (!slab->kbo) {
    461 	ret = -ENOMEM;
    462 	goto out_err0;
    463     }
    464 
    465     numBuffers = slab->kbo->actualSize / header->bufSize;
    466 
    467     slab->buffers = calloc(numBuffers, sizeof(*slab->buffers));
    468     if (!slab->buffers) {
    469 	ret = -ENOMEM;
    470 	goto out_err1;
    471     }
    472 
    473     WSBMINITLISTHEAD(&slab->head);
    474     WSBMINITLISTHEAD(&slab->freeBuffers);
    475     slab->numBuffers = numBuffers;
    476     slab->numFree = 0;
    477     slab->header = header;
    478 
    479     sBuf = slab->buffers;
    480     for (i = 0; i < numBuffers; ++i) {
    481 	ret = wsbmBufStorageInit(&sBuf->storage, &header->slabPool->pool);
    482 	if (ret)
    483 	    goto out_err2;
    484 	sBuf->parent = slab;
    485 	sBuf->start = i * header->bufSize;
    486 	sBuf->virtual = (void *)((uint8_t *) slab->kbo->virtual +
    487 				 sBuf->start);
    488 	wsbmAtomicSet(&sBuf->writers, 0);
    489 	sBuf->isSlabBuffer = 1;
    490 	WSBM_COND_INIT(&sBuf->event);
    491 	WSBMLISTADDTAIL(&sBuf->head, &slab->freeBuffers);
    492 	slab->numFree++;
    493 	sBuf++;
    494     }
    495 
    496     WSBMLISTADDTAIL(&slab->head, &header->slabs);
    497 
    498     return 0;
    499 
    500   out_err2:
    501     sBuf = slab->buffers;
    502     for (i = 0; i < numBuffers; ++i) {
    503 	if (sBuf->parent == slab) {
    504 	    WSBM_COND_FREE(&sBuf->event);
    505 	    wsbmBufStorageTakedown(&sBuf->storage);
    506 	}
    507 	sBuf++;
    508     }
    509     free(slab->buffers);
    510   out_err1:
    511     wsbmSetKernelBOFree(header->slabPool->cache, slab->kbo);
    512   out_err0:
    513     free(slab);
    514     return ret;
    515 }
    516 
    517 /*
    518  * Delete a buffer from the slab header delayed list and put
    519  * it on the slab free list.
    520  */
    521 
    522 static void
    523 wsbmSlabFreeBufferLocked(struct _WsbmSlabBuffer *buf)
    524 {
    525     struct _WsbmSlab *slab = buf->parent;
    526     struct _WsbmSlabSizeHeader *header = slab->header;
    527     struct _WsbmListHead *list = &buf->head;
    528 
    529     WSBMLISTDEL(list);
    530     WSBMLISTADDTAIL(list, &slab->freeBuffers);
    531     slab->numFree++;
    532 
    533     if (slab->head.next == &slab->head)
    534 	WSBMLISTADDTAIL(&slab->head, &header->slabs);
    535 
    536     if (slab->numFree == slab->numBuffers) {
    537 	list = &slab->head;
    538 	WSBMLISTDEL(list);
    539 	WSBMLISTADDTAIL(list, &header->freeSlabs);
    540     }
    541 
    542     if (header->slabs.next == &header->slabs ||
    543 	slab->numFree != slab->numBuffers) {
    544 
    545 	struct _WsbmListHead *next;
    546 	struct _WsbmSlabCache *cache = header->slabPool->cache;
    547 
    548 	WSBMLISTFOREACHSAFE(list, next, &header->freeSlabs) {
    549 	    uint32_t i;
    550 	    struct _WsbmSlabBuffer *sBuf;
    551 
    552 	    slab = WSBMLISTENTRY(list, struct _WsbmSlab, head);
    553 
    554 	    WSBMLISTDELINIT(list);
    555 
    556 	    sBuf = slab->buffers;
    557 	    for (i = 0; i < slab->numBuffers; ++i) {
    558 		if (sBuf->parent == slab) {
    559 		    WSBM_COND_FREE(&sBuf->event);
    560 		    wsbmBufStorageTakedown(&sBuf->storage);
    561 		}
    562 		sBuf++;
    563 	    }
    564 	    wsbmSetKernelBOFree(cache, slab->kbo);
    565 	    free(slab->buffers);
    566 	    free(slab);
    567 	}
    568     }
    569 }
    570 
    571 static void
    572 wsbmSlabCheckFreeLocked(struct _WsbmSlabSizeHeader *header, int wait)
    573 {
    574   struct _WsbmListHead *list, *prev, *first, *head;
    575     struct _WsbmSlabBuffer *sBuf;
    576     struct _WsbmSlab *slab;
    577     int firstWasSignaled = 1;
    578     int signaled;
    579     uint32_t i;
    580     int ret;
    581 
    582     /*
    583      * Rerun the freeing test if the youngest tested buffer
    584      * was signaled, since there might be more idle buffers
    585      * in the delay list.
    586      */
    587 
    588     while (firstWasSignaled) {
    589 	firstWasSignaled = 0;
    590 	signaled = 0;
    591 	first = header->delayedBuffers.next;
    592 
    593 	/* Only examine the oldest 1/3 of delayed buffers:
    594 	 */
    595 	if (header->numDelayed > 3) {
    596 	    for (i = 0; i < header->numDelayed; i += 3) {
    597 		first = first->next;
    598 	    }
    599 	}
    600 
    601 	/*
    602 	 * No need to take the buffer mutex for each buffer we loop
    603 	 * through since we're currently the only user.
    604 	 */
    605 
    606 	head = first->next;
    607 	WSBMLISTFOREACHPREVSAFE(list, prev, head) {
    608 
    609 	    if (list == &header->delayedBuffers)
    610 		break;
    611 
    612 	    sBuf = WSBMLISTENTRY(list, struct _WsbmSlabBuffer, head);
    613 
    614 	    slab = sBuf->parent;
    615 
    616 	    if (!signaled) {
    617 		if (wait) {
    618 		    ret = wsbmFenceFinish(sBuf->fence, sBuf->fenceType, 0);
    619 		    if (ret)
    620 			break;
    621 		    signaled = 1;
    622 		    wait = 0;
    623 		} else {
    624 		    signaled =
    625 			wsbmFenceSignaled(sBuf->fence, sBuf->fenceType);
    626 #ifdef DEBUG_FENCESIGNALED
    627 		    fencesignaled++;
    628 #endif
    629 		}
    630 		if (signaled) {
    631 		    if (list == first)
    632 			firstWasSignaled = 1;
    633 		    wsbmFenceUnreference(&sBuf->fence);
    634 		    header->numDelayed--;
    635 		    wsbmSlabFreeBufferLocked(sBuf);
    636 		} else
    637 		    break;
    638 	    } else if (wsbmFenceSignaledCached(sBuf->fence, sBuf->fenceType)) {
    639 		wsbmFenceUnreference(&sBuf->fence);
    640 		header->numDelayed--;
    641 		wsbmSlabFreeBufferLocked(sBuf);
    642 	    }
    643 	}
    644     }
    645 }
    646 
    647 static struct _WsbmSlabBuffer *
    648 wsbmSlabAllocBuffer(struct _WsbmSlabSizeHeader *header)
    649 {
    650     static struct _WsbmSlabBuffer *buf;
    651     struct _WsbmSlab *slab;
    652     struct _WsbmListHead *list;
    653     int count = WSBM_SLABPOOL_ALLOC_RETRIES;
    654 
    655     WSBM_MUTEX_LOCK(&header->mutex);
    656     while (header->slabs.next == &header->slabs && count > 0) {
    657 	wsbmSlabCheckFreeLocked(header, 0);
    658 	if (header->slabs.next != &header->slabs)
    659 	    break;
    660 
    661 	WSBM_MUTEX_UNLOCK(&header->mutex);
    662 	if (count != WSBM_SLABPOOL_ALLOC_RETRIES)
    663 	    usleep(1000);
    664 	WSBM_MUTEX_LOCK(&header->mutex);
    665 	(void)wsbmAllocSlab(header);
    666 	count--;
    667     }
    668 
    669     list = header->slabs.next;
    670     if (list == &header->slabs) {
    671 	WSBM_MUTEX_UNLOCK(&header->mutex);
    672 	return NULL;
    673     }
    674     slab = WSBMLISTENTRY(list, struct _WsbmSlab, head);
    675     if (--slab->numFree == 0)
    676 	WSBMLISTDELINIT(list);
    677 
    678     list = slab->freeBuffers.next;
    679     WSBMLISTDELINIT(list);
    680 
    681     WSBM_MUTEX_UNLOCK(&header->mutex);
    682     buf = WSBMLISTENTRY(list, struct _WsbmSlabBuffer, head);
    683 
    684     buf->storage.destroyContainer = NULL;
    685 
    686 #ifdef DEBUG_FENCESIGNALED
    687     createbuffer++;
    688 #endif
    689     return buf;
    690 }
    691 
    692 static struct _WsbmBufStorage *
    693 pool_create(struct _WsbmBufferPool *pool, unsigned long size,
    694 	    uint32_t placement, unsigned alignment)
    695 {
    696     struct _WsbmSlabPool *slabPool = slabPoolFromPool(pool);
    697     struct _WsbmSlabSizeHeader *header;
    698     struct _WsbmSlabBuffer *sBuf;
    699     uint32_t i;
    700     int ret;
    701 
    702     /*
    703      * FIXME: Check for compatibility.
    704      */
    705 
    706     header = slabPool->headers;
    707     for (i = 0; i < slabPool->numBuckets; ++i) {
    708 	if (header->bufSize >= size)
    709 	    break;
    710 	header++;
    711     }
    712 
    713     if (i < slabPool->numBuckets) {
    714 	sBuf = wsbmSlabAllocBuffer(header);
    715 	return ((sBuf) ? &sBuf->storage : NULL);
    716     }
    717 
    718     /*
    719      * Fall back to allocate a buffer object directly from DRM.
    720      * and wrap it in a wsbmBO structure.
    721      */
    722 
    723     sBuf = calloc(1, sizeof(*sBuf));
    724 
    725     if (!sBuf)
    726 	return NULL;
    727 
    728     if (alignment) {
    729 	if ((alignment < slabPool->pageSize)
    730 	    && (slabPool->pageSize % alignment))
    731 	    goto out_err0;
    732 	if ((alignment > slabPool->pageSize)
    733 	    && (alignment % slabPool->pageSize))
    734 	    goto out_err0;
    735     }
    736 
    737     ret = wsbmBufStorageInit(&sBuf->storage, pool);
    738     if (ret)
    739 	goto out_err0;
    740 
    741     ret = WSBM_COND_INIT(&sBuf->event);
    742     if (ret)
    743 	goto out_err1;
    744 
    745     {
    746 	union ttm_pl_create_arg arg;
    747 
    748 	arg.req.size = size;
    749 	arg.req.placement = placement;
    750 	arg.req.page_alignment = alignment / slabPool->pageSize;
    751 
    752 	DRMRESTARTCOMMANDWRITEREAD(pool->fd,
    753 				   slabPool->devOffset + TTM_PL_CREATE,
    754 				   arg, ret);
    755 
    756 	if (ret)
    757 	    goto out_err2;
    758 
    759 	sBuf->kBuf.gpuOffset = arg.rep.gpu_offset;
    760 	sBuf->kBuf.placement = arg.rep.placement;
    761 	sBuf->kBuf.handle = arg.rep.handle;
    762 	sBuf->mapHandle = arg.rep.map_handle;
    763 	sBuf->requestedSize = size;
    764 
    765 	sBuf->virtual = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED,
    766 			     pool->fd, sBuf->mapHandle);
    767 
    768 	if (sBuf->virtual == MAP_FAILED)
    769 	    goto out_err3;
    770     }
    771 
    772     wsbmAtomicSet(&sBuf->writers, 0);
    773     return &sBuf->storage;
    774   out_err3:
    775     {
    776 	struct ttm_pl_reference_req arg;
    777 
    778 	arg.handle = sBuf->kBuf.handle;
    779 	(void)drmCommandWriteRead(pool->fd,
    780 				  slabPool->devOffset + TTM_PL_UNREF,
    781 				  &arg, sizeof(arg));
    782     }
    783   out_err2:
    784     WSBM_COND_FREE(&sBuf->event);
    785   out_err1:
    786     wsbmBufStorageTakedown(&sBuf->storage);
    787   out_err0:
    788     free(sBuf);
    789     return NULL;
    790 }
    791 
    792 static void
    793 pool_destroy(struct _WsbmBufStorage **p_buf)
    794 {
    795     struct _WsbmBufStorage *buf = *p_buf;
    796     struct _WsbmSlabBuffer *sBuf = slabBuffer(buf);
    797     struct _WsbmSlab *slab;
    798     struct _WsbmSlabSizeHeader *header;
    799 
    800     *p_buf = NULL;
    801 
    802     if (!sBuf->isSlabBuffer) {
    803 	struct _WsbmSlabPool *slabPool = slabPoolFromBuf(sBuf);
    804 	struct ttm_pl_reference_req arg;
    805 
    806 	if (sBuf->virtual != NULL) {
    807 	    (void)munmap(sBuf->virtual, sBuf->requestedSize);
    808 	    sBuf->virtual = NULL;
    809 	}
    810 
    811 	arg.handle = sBuf->kBuf.handle;
    812 	(void)drmCommandWrite(slabPool->pool.fd,
    813 			      slabPool->devOffset + TTM_PL_UNREF,
    814 			      &arg, sizeof(arg));
    815 
    816 	WSBM_COND_FREE(&sBuf->event);
    817 	wsbmBufStorageTakedown(&sBuf->storage);
    818 	free(sBuf);
    819 	return;
    820     }
    821 
    822     slab = sBuf->parent;
    823     header = slab->header;
    824 
    825     /*
    826      * No need to take the buffer mutex below since we're the only user.
    827      */
    828 
    829     WSBM_MUTEX_LOCK(&header->mutex);
    830     sBuf->unFenced = 0;
    831     wsbmAtomicSet(&sBuf->writers, 0);
    832     wsbmAtomicSet(&sBuf->storage.refCount, 1);
    833 
    834     if (sBuf->fence && !wsbmFenceSignaledCached(sBuf->fence, sBuf->fenceType)) {
    835 	WSBMLISTADDTAIL(&sBuf->head, &header->delayedBuffers);
    836 	header->numDelayed++;
    837     } else {
    838 	if (sBuf->fence)
    839 	    wsbmFenceUnreference(&sBuf->fence);
    840 	wsbmSlabFreeBufferLocked(sBuf);
    841     }
    842 
    843     WSBM_MUTEX_UNLOCK(&header->mutex);
    844 }
    845 
    846 static void
    847 waitIdleLocked(struct _WsbmSlabBuffer *sBuf, int lazy)
    848 {
    849     struct _WsbmBufStorage *storage = &sBuf->storage;
    850 
    851     while (sBuf->unFenced || sBuf->fence != NULL) {
    852 
    853 	if (sBuf->unFenced)
    854 	    WSBM_COND_WAIT(&sBuf->event, &storage->mutex);
    855 
    856 	if (sBuf->fence != NULL) {
    857 	    if (!wsbmFenceSignaled(sBuf->fence, sBuf->fenceType)) {
    858 		struct _WsbmFenceObject *fence =
    859 		    wsbmFenceReference(sBuf->fence);
    860 
    861 		WSBM_MUTEX_UNLOCK(&storage->mutex);
    862 		(void)wsbmFenceFinish(fence, sBuf->fenceType, lazy);
    863 		WSBM_MUTEX_LOCK(&storage->mutex);
    864 		if (sBuf->fence == fence)
    865 		    wsbmFenceUnreference(&sBuf->fence);
    866 
    867 		wsbmFenceUnreference(&fence);
    868 	    } else {
    869 		wsbmFenceUnreference(&sBuf->fence);
    870 	    }
    871 	}
    872     }
    873 }
    874 
    875 static int
    876 pool_waitIdle(struct _WsbmBufStorage *buf, int lazy)
    877 {
    878     struct _WsbmSlabBuffer *sBuf = slabBuffer(buf);
    879 
    880     WSBM_MUTEX_LOCK(&buf->mutex);
    881     waitIdleLocked(sBuf, lazy);
    882     WSBM_MUTEX_UNLOCK(&buf->mutex);
    883 
    884     return 0;
    885 }
    886 
    887 static int
    888 pool_map(struct _WsbmBufStorage *buf, unsigned mode __attribute__ ((unused)), void **virtual)
    889 {
    890     struct _WsbmSlabBuffer *sBuf = slabBuffer(buf);
    891 
    892     *virtual = sBuf->virtual;
    893 
    894     return 0;
    895 }
    896 
    897 static void
    898 pool_releaseFromCpu(struct _WsbmBufStorage *buf, unsigned mode __attribute__ ((unused)))
    899 {
    900     struct _WsbmSlabBuffer *sBuf = slabBuffer(buf);
    901 
    902     if (wsbmAtomicDecZero(&sBuf->writers))
    903 	WSBM_COND_BROADCAST(&sBuf->event);
    904 }
    905 
    906 static int
    907 pool_syncForCpu(struct _WsbmBufStorage *buf, unsigned mode)
    908 {
    909     struct _WsbmSlabBuffer *sBuf = slabBuffer(buf);
    910     int ret = 0;
    911 
    912     WSBM_MUTEX_LOCK(&buf->mutex);
    913     if ((mode & WSBM_SYNCCPU_DONT_BLOCK)) {
    914 	int signaled;
    915 
    916 	if (sBuf->unFenced) {
    917 	    ret = -EBUSY;
    918 	    goto out_unlock;
    919 	}
    920 
    921 	if (sBuf->isSlabBuffer)
    922 	    signaled = (sBuf->fence == NULL) ||
    923 		wsbmFenceSignaledCached(sBuf->fence, sBuf->fenceType);
    924 	else
    925 	    signaled = (sBuf->fence == NULL) ||
    926 		wsbmFenceSignaled(sBuf->fence, sBuf->fenceType);
    927 
    928 	ret = 0;
    929 	if (signaled) {
    930 	    wsbmFenceUnreference(&sBuf->fence);
    931 	    wsbmAtomicInc(&sBuf->writers);
    932 	} else
    933 	    ret = -EBUSY;
    934 	goto out_unlock;
    935     }
    936     waitIdleLocked(sBuf, 0);
    937     wsbmAtomicInc(&sBuf->writers);
    938   out_unlock:
    939     WSBM_MUTEX_UNLOCK(&buf->mutex);
    940     return ret;
    941 }
    942 
    943 static void
    944 pool_unmap(struct _WsbmBufStorage *buf __attribute__ ((unused)))
    945 {
    946     ;
    947 }
    948 
    949 static unsigned long
    950 pool_poolOffset(struct _WsbmBufStorage *buf)
    951 {
    952     struct _WsbmSlabBuffer *sBuf = slabBuffer(buf);
    953 
    954     return sBuf->start;
    955 }
    956 
    957 static unsigned long
    958 pool_size(struct _WsbmBufStorage *buf)
    959 {
    960     struct _WsbmSlabBuffer *sBuf = slabBuffer(buf);
    961 
    962     if (!sBuf->isSlabBuffer)
    963 	return sBuf->requestedSize;
    964 
    965     return sBuf->parent->header->bufSize;
    966 }
    967 
    968 static struct _WsbmKernelBuf *
    969 pool_kernel(struct _WsbmBufStorage *buf)
    970 {
    971     struct _WsbmSlabBuffer *sBuf = slabBuffer(buf);
    972 
    973     return (sBuf->isSlabBuffer) ? &sBuf->parent->kbo->kBuf : &sBuf->kBuf;
    974 }
    975 
    976 static unsigned long
    977 pool_offset(struct _WsbmBufStorage *buf)
    978 {
    979     return pool_kernel(buf)->gpuOffset + pool_poolOffset(buf);
    980 }
    981 
    982 static void
    983 pool_fence(struct _WsbmBufStorage *buf, struct _WsbmFenceObject *fence)
    984 {
    985     struct _WsbmSlabBuffer *sBuf = slabBuffer(buf);
    986     struct _WsbmKernelBuf *kBuf;
    987 
    988     WSBM_MUTEX_LOCK(&buf->mutex);
    989     if (sBuf->fence)
    990 	wsbmFenceUnreference(&sBuf->fence);
    991 
    992     kBuf = pool_kernel(buf);
    993     sBuf->fenceType = kBuf->fence_type_mask;
    994     if (!wsbmFenceSignaledCached(fence, sBuf->fenceType))
    995 	sBuf->fence = wsbmFenceReference(fence);
    996 
    997     sBuf->unFenced = 0;
    998     WSBM_COND_BROADCAST(&sBuf->event);
    999     WSBM_MUTEX_UNLOCK(&buf->mutex);
   1000 }
   1001 
   1002 static int
   1003 pool_validate(struct _WsbmBufStorage *buf,
   1004         uint64_t set_flags __attribute__ ((unused)), uint64_t clr_flags __attribute__ ((unused)))
   1005 {
   1006     struct _WsbmSlabBuffer *sBuf = slabBuffer(buf);
   1007 
   1008     WSBM_MUTEX_LOCK(&buf->mutex);
   1009     while (wsbmAtomicRead(&sBuf->writers) != 0) {
   1010 	WSBM_COND_WAIT(&sBuf->event, &buf->mutex);
   1011     }
   1012 
   1013     sBuf->unFenced = 1;
   1014     WSBM_MUTEX_UNLOCK(&buf->mutex);
   1015     return 0;
   1016 }
   1017 
   1018 static void
   1019 pool_unvalidate(struct _WsbmBufStorage *buf)
   1020 {
   1021     struct _WsbmSlabBuffer *sBuf = slabBuffer(buf);
   1022 
   1023     WSBM_MUTEX_LOCK(&buf->mutex);
   1024     if (sBuf->unFenced) {
   1025 	sBuf->unFenced = 0;
   1026 	WSBM_COND_BROADCAST(&sBuf->event);
   1027     }
   1028     WSBM_MUTEX_UNLOCK(&buf->mutex);
   1029 }
   1030 
   1031 struct _WsbmSlabCache *
   1032 wsbmSlabCacheInit(uint32_t checkIntervalMsec, uint32_t slabTimeoutMsec)
   1033 {
   1034     struct _WsbmSlabCache *tmp;
   1035 
   1036     tmp = calloc(1, sizeof(*tmp));
   1037     if (!tmp)
   1038 	return NULL;
   1039 
   1040     WSBM_MUTEX_INIT(&tmp->mutex);
   1041     WSBM_MUTEX_LOCK(&tmp->mutex);
   1042     tmp->slabTimeout.tv_usec = slabTimeoutMsec * 1000;
   1043     tmp->slabTimeout.tv_sec = tmp->slabTimeout.tv_usec / 1000000;
   1044     tmp->slabTimeout.tv_usec -= tmp->slabTimeout.tv_sec * 1000000;
   1045 
   1046     tmp->checkInterval.tv_usec = checkIntervalMsec * 1000;
   1047     tmp->checkInterval.tv_sec = tmp->checkInterval.tv_usec / 1000000;
   1048     tmp->checkInterval.tv_usec -= tmp->checkInterval.tv_sec * 1000000;
   1049 
   1050     gettimeofday(&tmp->nextCheck, NULL);
   1051     wsbmTimeAdd(&tmp->nextCheck, &tmp->checkInterval);
   1052     WSBMINITLISTHEAD(&tmp->timeoutList);
   1053     WSBMINITLISTHEAD(&tmp->unCached);
   1054     WSBMINITLISTHEAD(&tmp->cached);
   1055     WSBM_MUTEX_UNLOCK(&tmp->mutex);
   1056 
   1057     return tmp;
   1058 }
   1059 
   1060 void
   1061 wsbmSlabCacheFinish(struct _WsbmSlabCache *cache)
   1062 {
   1063     struct timeval time;
   1064 
   1065     time = cache->nextCheck;
   1066     WSBM_MUTEX_LOCK(&cache->mutex);
   1067     wsbmTimeAdd(&time, &cache->checkInterval);
   1068     wsbmFreeTimeoutKBOsLocked(cache, &time);
   1069     WSBM_MUTEX_UNLOCK(&cache->mutex);
   1070 
   1071     assert(cache->timeoutList.next == &cache->timeoutList);
   1072     assert(cache->unCached.next == &cache->unCached);
   1073     assert(cache->cached.next == &cache->cached);
   1074 
   1075     WSBM_MUTEX_FREE(&cache->mutex);
   1076     free(cache);
   1077 }
   1078 
   1079 static void
   1080 wsbmInitSizeHeader(struct _WsbmSlabPool *slabPool, uint32_t size,
   1081 		   struct _WsbmSlabSizeHeader *header)
   1082 {
   1083     WSBM_MUTEX_INIT(&header->mutex);
   1084     WSBM_MUTEX_LOCK(&header->mutex);
   1085 
   1086     WSBMINITLISTHEAD(&header->slabs);
   1087     WSBMINITLISTHEAD(&header->freeSlabs);
   1088     WSBMINITLISTHEAD(&header->delayedBuffers);
   1089 
   1090     header->numDelayed = 0;
   1091     header->slabPool = slabPool;
   1092     header->bufSize = size;
   1093 
   1094     WSBM_MUTEX_UNLOCK(&header->mutex);
   1095 }
   1096 
   1097 static void
   1098 wsbmFinishSizeHeader(struct _WsbmSlabSizeHeader *header)
   1099 {
   1100     struct _WsbmListHead *list, *next;
   1101     struct _WsbmSlabBuffer *sBuf;
   1102 
   1103     WSBM_MUTEX_LOCK(&header->mutex);
   1104     WSBMLISTFOREACHSAFE(list, next, &header->delayedBuffers) {
   1105 	sBuf = WSBMLISTENTRY(list, struct _WsbmSlabBuffer, head);
   1106 
   1107 	if (sBuf->fence) {
   1108 	    (void)wsbmFenceFinish(sBuf->fence, sBuf->fenceType, 0);
   1109 	    wsbmFenceUnreference(&sBuf->fence);
   1110 	}
   1111 	header->numDelayed--;
   1112 	wsbmSlabFreeBufferLocked(sBuf);
   1113     }
   1114     WSBM_MUTEX_UNLOCK(&header->mutex);
   1115     WSBM_MUTEX_FREE(&header->mutex);
   1116 }
   1117 
   1118 static void
   1119 pool_takedown(struct _WsbmBufferPool *pool)
   1120 {
   1121     struct _WsbmSlabPool *slabPool = slabPoolFromPool(pool);
   1122     unsigned int i;
   1123 
   1124     for (i = 0; i < slabPool->numBuckets; ++i) {
   1125 	wsbmFinishSizeHeader(&slabPool->headers[i]);
   1126     }
   1127 
   1128     free(slabPool->headers);
   1129     free(slabPool->bucketSizes);
   1130     free(slabPool);
   1131 }
   1132 
   1133 struct _WsbmBufferPool *
   1134 wsbmSlabPoolInit(int fd,
   1135 		 uint32_t devOffset,
   1136 		 uint32_t placement,
   1137 		 uint32_t validMask,
   1138 		 uint32_t smallestSize,
   1139 		 uint32_t numSizes,
   1140 		 uint32_t desiredNumBuffers,
   1141 		 uint32_t maxSlabSize,
   1142 		 uint32_t pageAlignment, struct _WsbmSlabCache *cache)
   1143 {
   1144     struct _WsbmBufferPool *pool;
   1145     struct _WsbmSlabPool *slabPool;
   1146     uint32_t i;
   1147 
   1148     slabPool = calloc(1, sizeof(*slabPool));
   1149     if (!slabPool)
   1150 	return NULL;
   1151 
   1152     pool = &slabPool->pool;
   1153 
   1154     slabPool->bucketSizes = calloc(numSizes, sizeof(*slabPool->bucketSizes));
   1155     if (!slabPool->bucketSizes)
   1156 	goto out_err0;
   1157 
   1158     slabPool->headers = calloc(numSizes, sizeof(*slabPool->headers));
   1159     if (!slabPool->headers)
   1160 	goto out_err1;
   1161 
   1162     slabPool->devOffset = devOffset;
   1163     slabPool->cache = cache;
   1164     slabPool->proposedPlacement = placement;
   1165     slabPool->validMask = validMask;
   1166     slabPool->numBuckets = numSizes;
   1167     slabPool->pageSize = getpagesize();
   1168     slabPool->pageAlignment = pageAlignment;
   1169     slabPool->maxSlabSize = maxSlabSize;
   1170     slabPool->desiredNumBuffers = desiredNumBuffers;
   1171 
   1172     for (i = 0; i < slabPool->numBuckets; ++i) {
   1173 	slabPool->bucketSizes[i] = (smallestSize << i);
   1174 	wsbmInitSizeHeader(slabPool, slabPool->bucketSizes[i],
   1175 			   &slabPool->headers[i]);
   1176     }
   1177 
   1178     pool->fd = fd;
   1179     pool->map = &pool_map;
   1180     pool->unmap = &pool_unmap;
   1181     pool->destroy = &pool_destroy;
   1182     pool->offset = &pool_offset;
   1183     pool->poolOffset = &pool_poolOffset;
   1184     pool->size = &pool_size;
   1185     pool->create = &pool_create;
   1186     pool->fence = &pool_fence;
   1187     pool->kernel = &pool_kernel;
   1188     pool->validate = &pool_validate;
   1189     pool->unvalidate = &pool_unvalidate;
   1190     pool->waitIdle = &pool_waitIdle;
   1191     pool->takeDown = &pool_takedown;
   1192     pool->releasefromcpu = &pool_releaseFromCpu;
   1193     pool->syncforcpu = &pool_syncForCpu;
   1194 
   1195     return pool;
   1196 
   1197   out_err1:
   1198     free(slabPool->bucketSizes);
   1199   out_err0:
   1200     free(slabPool);
   1201 
   1202     return NULL;
   1203 }
   1204