diff options
Diffstat (limited to 'src/wsbm_slabpool_new.c')
-rw-r--r-- | src/wsbm_slabpool_new.c | 1213 |
1 files changed, 1213 insertions, 0 deletions
diff --git a/src/wsbm_slabpool_new.c b/src/wsbm_slabpool_new.c new file mode 100644 index 0000000..92ffb17 --- /dev/null +++ b/src/wsbm_slabpool_new.c @@ -0,0 +1,1213 @@ +/************************************************************************** + * + * Copyright 2006-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * + **************************************************************************/ +/* + * Authors: Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ttm/ttm_placement_user.h> +#include <stdint.h> +#include <sys/time.h> +#include <errno.h> +#include <unistd.h> +#include <assert.h> +#include <string.h> +#include <sys/mman.h> +#include <xf86drm.h> +#include "wsbm_pool.h" +#include "wsbm_fencemgr.h" +#include "wsbm_priv.h" +#include "wsbm_manager.h" +#include <stdio.h> + + +#define WSBM_SLABPOOL_ALLOC_RETRIES 100 +#define DRMRESTARTCOMMANDWRITE(_fd, _val, _arg, _ret) \ + do { \ + (_ret) = drmCommandWrite(_fd, _val, &(_arg), sizeof(_arg)); \ + } while ((_ret) == -EAGAIN || (_ret) == -ERESTART); \ + +#define DRMRESTARTCOMMANDWRITEREAD(_fd, _val, _arg, _ret) \ + do { \ + (_ret) = drmCommandWriteRead(_fd, _val, &(_arg), sizeof(_arg)); \ + } while ((_ret) == -EAGAIN || (_ret) == -ERESTART); \ + + +#ifdef DEBUG_FENCESIGNALED +static int createbuffer = 0; +static int fencesignaled = 0; +#endif + +struct _WsbmSlab; + +struct _WsbmSlabBuffer { + struct _WsbmKernelBuf kBuf; + struct _WsbmBufStorage storage; + struct _WsbmCond event; + + /* + * Remains constant after creation. + */ + + int isSlabBuffer; + struct _WsbmSlab *parent; + uint32_t start; + void *virtual; + unsigned long requestedSize; + uint64_t mapHandle; + + /* + * Protected by struct _WsbmSlabSizeHeader::mutex + */ + + struct _WsbmListHead head; + + /* + * Protected by this::mutex + */ + + struct _WsbmFenceObject *fence; + uint32_t fenceType; + struct _WsbmAtomic writers; /* (Only upping) */ + int unFenced; +}; + +struct _WsbmSlabPool; +struct _WsbmSlabKernelBO { + + /* + * Constant at creation + */ + + struct _WsbmKernelBuf kBuf; + uint32_t pageAlignment; + void *virtual; + unsigned long actualSize; + uint64_t mapHandle; + + /* + * Protected by struct _WsbmSlabCache::mutex + */ + + struct _WsbmSlabPool *slabPool; + uint32_t proposedPlacement; + struct _WsbmListHead timeoutHead; + struct _WsbmListHead head; + struct timeval timeFreed; +}; + +struct _WsbmSlab{ + struct _WsbmListHead head; + struct _WsbmListHead freeBuffers; + uint32_t numBuffers; + uint32_t numFree; + struct _WsbmSlabBuffer *buffers; + struct _WsbmSlabSizeHeader *header; + struct _WsbmSlabKernelBO *kbo; +}; + + +struct _WsbmSlabSizeHeader { + /* + * Constant at creation. + */ + struct _WsbmSlabPool *slabPool; + uint32_t bufSize; + + /* + * Protected by this::mutex + */ + + struct _WsbmListHead slabs; + struct _WsbmListHead freeSlabs; + struct _WsbmListHead delayedBuffers; + uint32_t numDelayed; + struct _WsbmMutex mutex; +}; + +struct _WsbmSlabCache { + struct timeval slabTimeout; + struct timeval checkInterval; + struct timeval nextCheck; + struct _WsbmListHead timeoutList; + struct _WsbmListHead unCached; + struct _WsbmListHead cached; + struct _WsbmMutex mutex; +}; + + +struct _WsbmSlabPool { + struct _WsbmBufferPool pool; + + /* + * The data of this structure remains constant after + * initialization and thus needs no mutex protection. + */ + + unsigned int devOffset; + struct _WsbmSlabCache *cache; + uint32_t proposedPlacement; + uint32_t validMask; + uint32_t *bucketSizes; + uint32_t numBuckets; + uint32_t pageSize; + int pageAlignment; + int maxSlabSize; + int desiredNumBuffers; + struct _WsbmSlabSizeHeader *headers; +}; + +static inline struct _WsbmSlabPool * +slabPoolFromPool(struct _WsbmBufferPool *pool) +{ + return containerOf(pool, struct _WsbmSlabPool , pool); +} + +static inline struct _WsbmSlabPool * +slabPoolFromBuf(struct _WsbmSlabBuffer *sBuf) +{ + return slabPoolFromPool(sBuf->storage.pool); +} + +static inline struct _WsbmSlabBuffer * +slabBuffer(struct _WsbmBufStorage *buf) +{ + return containerOf(buf, struct _WsbmSlabBuffer, storage); +} + + +/* + * FIXME: Perhaps arrange timeout slabs in size buckets for fast + * retreival?? + */ + + +static inline int +wsbmTimeAfterEq(struct timeval *arg1, struct timeval *arg2) +{ + return ((arg1->tv_sec > arg2->tv_sec) || + ((arg1->tv_sec == arg2->tv_sec) && + (arg1->tv_usec > arg2->tv_usec))); +} + +static inline void +wsbmTimeAdd(struct timeval *arg, struct timeval *add) +{ + unsigned int sec; + + arg->tv_sec += add->tv_sec; + arg->tv_usec += add->tv_usec; + sec = arg->tv_usec / 1000000; + arg->tv_sec += sec; + arg->tv_usec -= sec*1000000; +} + +#include <stdio.h> + +static void +wsbmFreeKernelBO(struct _WsbmSlabKernelBO *kbo) +{ + struct ttm_pl_reference_req arg; + struct _WsbmSlabPool *slabPool; + + if (!kbo) + return; + + fprintf(stderr, "Free kbo size %d\n", kbo->actualSize); + slabPool = kbo->slabPool; + arg.handle = kbo->kBuf.handle; + (void) munmap(kbo->virtual, kbo->actualSize); + (void) drmCommandWrite(slabPool->pool.fd, slabPool->devOffset + TTM_PL_UNREF, + &arg, sizeof(arg)); + free(kbo); +} + + +static void +wsbmFreeTimeoutKBOsLocked(struct _WsbmSlabCache *cache, + struct timeval *time) +{ + struct _WsbmListHead *list, *next; + struct _WsbmSlabKernelBO *kbo; + + if (!wsbmTimeAfterEq(time, &cache->nextCheck)) + return; + + WSBMLISTFOREACHSAFE(list, next, &cache->timeoutList) { + kbo = WSBMLISTENTRY(list, struct _WsbmSlabKernelBO, timeoutHead); + + if (!wsbmTimeAfterEq(time, &kbo->timeFreed)) + break; + + WSBMLISTDELINIT(&kbo->timeoutHead); + WSBMLISTDELINIT(&kbo->head); + wsbmFreeKernelBO(kbo); + } + + cache->nextCheck = *time; + wsbmTimeAdd(&cache->nextCheck, &cache->checkInterval); +} + + +/* + * Add a _SlabKernelBO to the free slab manager. + * This means that it is available for reuse, but if it's not + * reused in a while, it will be freed. + */ + +static void +wsbmSetKernelBOFree(struct _WsbmSlabCache *cache, + struct _WsbmSlabKernelBO *kbo) +{ + struct timeval time; + struct timeval timeFreed; + + gettimeofday(&time, NULL); + timeFreed = time; + WSBM_MUTEX_LOCK(&cache->mutex); + wsbmTimeAdd(&timeFreed, &cache->slabTimeout); + kbo->timeFreed = timeFreed; + + if (kbo->kBuf.placement & TTM_PL_FLAG_CACHED) + WSBMLISTADD(&kbo->head, &cache->cached); + else + WSBMLISTADD(&kbo->head, &cache->unCached); + + WSBMLISTADDTAIL(&kbo->timeoutHead, &cache->timeoutList); + wsbmFreeTimeoutKBOsLocked(cache, &time); + + WSBM_MUTEX_UNLOCK(&cache->mutex); +} + +/* + * Get a _SlabKernelBO for us to use as storage for a slab. + */ + + +static struct _WsbmSlabKernelBO * +wsbmAllocKernelBO(struct _WsbmSlabSizeHeader *header) + +{ + struct _WsbmSlabPool *slabPool = header->slabPool; + struct _WsbmSlabCache *cache = slabPool->cache; + struct _WsbmListHead *list, *head; + uint32_t size = header->bufSize * slabPool->desiredNumBuffers; + struct _WsbmSlabKernelBO *kbo; + struct _WsbmSlabKernelBO *kboTmp; + int ret; + + /* + * FIXME: We should perhaps allow some variation in slabsize in order + * to efficiently reuse slabs. + */ + + + size = (size <= slabPool->maxSlabSize) ? size : slabPool->maxSlabSize; + if (size < header->bufSize) + size = header->bufSize; + size = (size + slabPool->pageSize - 1) & ~(slabPool->pageSize - 1); + WSBM_MUTEX_LOCK(&cache->mutex); + + kbo = NULL; + + retry: + head = (slabPool->proposedPlacement & TTM_PL_FLAG_CACHED) ? + &cache->cached : &cache->unCached; + + WSBMLISTFOREACH(list, head) { + kboTmp = WSBMLISTENTRY(list, struct _WsbmSlabKernelBO, head); + if ((kboTmp->actualSize == size) && + (slabPool->pageAlignment == 0 || + (kboTmp->pageAlignment % slabPool->pageAlignment) == 0)) { + + if (!kbo) + kbo = kboTmp; + + if ((kbo->proposedPlacement ^ slabPool->proposedPlacement) == 0) + break; + + } + } + + if (kbo) { + WSBMLISTDELINIT(&kbo->head); + WSBMLISTDELINIT(&kbo->timeoutHead); + } + + WSBM_MUTEX_UNLOCK(&cache->mutex); + + if (kbo) { + uint32_t new_mask = kbo->proposedPlacement ^ slabPool->proposedPlacement; + + ret = 0; + if (new_mask) { + union ttm_pl_setstatus_arg arg; + struct ttm_pl_setstatus_req *req = &arg.req; + struct ttm_pl_rep *rep = &arg.rep; + + req->handle = kbo->kBuf.handle; + req->set_placement = slabPool->proposedPlacement & new_mask; + req->clr_placement = ~slabPool->proposedPlacement & new_mask; + DRMRESTARTCOMMANDWRITEREAD(slabPool->pool.fd, + slabPool->devOffset + TTM_PL_SETSTATUS, + arg, ret); + if (ret == 0) { + kbo->kBuf.gpuOffset = rep->gpu_offset; + kbo->kBuf.placement = rep->placement; + } + kbo->proposedPlacement = slabPool->proposedPlacement; + } + + if (ret == 0) + return kbo; + + wsbmFreeKernelBO(kbo); + kbo = NULL; + goto retry; + } + + fprintf(stderr, "New kbo 0x%08x size %d\n", + slabPool->proposedPlacement, size); + kbo = calloc(1, sizeof(*kbo)); + if (!kbo) + return NULL; + + { + union ttm_pl_create_arg arg; + + kbo->slabPool = slabPool; + WSBMINITLISTHEAD(&kbo->head); + WSBMINITLISTHEAD(&kbo->timeoutHead); + + arg.req.size = size; + arg.req.placement = slabPool->proposedPlacement; + arg.req.page_alignment = slabPool->pageAlignment; + + DRMRESTARTCOMMANDWRITEREAD(slabPool->pool.fd, + slabPool->devOffset + TTM_PL_CREATE, + arg, ret); + if (ret) + goto out_err0; + + kbo->kBuf.gpuOffset = arg.rep.gpu_offset; + kbo->kBuf.placement = arg.rep.placement; + kbo->kBuf.handle = arg.rep.handle; + + kbo->actualSize = arg.rep.bo_size; + kbo->mapHandle = arg.rep.map_handle; + kbo->proposedPlacement = slabPool->proposedPlacement; + } + + kbo->virtual = mmap(0, kbo->actualSize, + PROT_READ | PROT_WRITE, MAP_SHARED, + slabPool->pool.fd, kbo->mapHandle); + + if (kbo->virtual == MAP_FAILED) { + ret = -errno; + goto out_err1; + } + + return kbo; + + out_err1: + { + struct ttm_pl_reference_req arg = + {.handle = kbo->kBuf.handle}; + + (void) drmCommandWrite(slabPool->pool.fd, + slabPool->devOffset + TTM_PL_UNREF, + &arg, sizeof(arg)); + } + out_err0: + free(kbo); + return NULL; +} + + +static int +wsbmAllocSlab(struct _WsbmSlabSizeHeader *header) +{ + struct _WsbmSlab *slab; + struct _WsbmSlabBuffer *sBuf; + uint32_t numBuffers; + int ret; + int i; + + slab = calloc(1, sizeof(*slab)); + if (!slab) + return -ENOMEM; + + slab->kbo = wsbmAllocKernelBO(header); + if (!slab->kbo) { + ret = -ENOMEM; + goto out_err0; + } + + numBuffers = slab->kbo->actualSize / header->bufSize; + + slab->buffers = calloc(numBuffers, sizeof(*slab->buffers)); + if (!slab->buffers) { + ret = -ENOMEM; + goto out_err1; + } + + WSBMINITLISTHEAD(&slab->head); + WSBMINITLISTHEAD(&slab->freeBuffers); + slab->numBuffers = numBuffers; + slab->numFree = 0; + slab->header = header; + + sBuf = slab->buffers; + for (i=0; i < numBuffers; ++i) { + ret = wsbmBufStorageInit(&sBuf->storage, &header->slabPool->pool); + if (ret) + goto out_err2; + sBuf->parent = slab; + sBuf->start = i* header->bufSize; + sBuf->virtual = (void *) ((uint8_t *) slab->kbo->virtual + + sBuf->start); + wsbmAtomicSet(&sBuf->writers, 0); + sBuf->isSlabBuffer = 1; + WSBM_COND_INIT(&sBuf->event); + WSBMLISTADDTAIL(&sBuf->head, &slab->freeBuffers); + slab->numFree++; + sBuf++; + } + + WSBMLISTADDTAIL(&slab->head, &header->slabs); + + return 0; + + out_err2: + sBuf = slab->buffers; + for (i=0; i < numBuffers; ++i) { + if (sBuf->parent == slab) { + WSBM_COND_FREE(&sBuf->event); + wsbmBufStorageTakedown(&sBuf->storage); + } + sBuf++; + } + out_err1: + wsbmSetKernelBOFree(header->slabPool->cache, slab->kbo); + free(slab->buffers); + out_err0: + free(slab); + return ret; +} + +/* + * Delete a buffer from the slab header delayed list and put + * it on the slab free list. + */ + +static void +wsbmSlabFreeBufferLocked(struct _WsbmSlabBuffer *buf) +{ + struct _WsbmSlab *slab = buf->parent; + struct _WsbmSlabSizeHeader *header = slab->header; + struct _WsbmListHead *list = &buf->head; + + WSBMLISTDEL(list); + WSBMLISTADDTAIL(list, &slab->freeBuffers); + slab->numFree++; + + if (slab->head.next == &slab->head) + WSBMLISTADDTAIL(&slab->head, &header->slabs); + + if (slab->numFree == slab->numBuffers) { + list = &slab->head; + WSBMLISTDEL(list); + WSBMLISTADDTAIL(list, &header->freeSlabs); + } + + if (header->slabs.next == &header->slabs || + slab->numFree != slab->numBuffers) { + + struct _WsbmListHead *next; + struct _WsbmSlabCache *cache = header->slabPool->cache; + + WSBMLISTFOREACHSAFE(list, next, &header->freeSlabs) { + int i; + struct _WsbmSlabBuffer *sBuf; + + slab = WSBMLISTENTRY(list, struct _WsbmSlab, head); + WSBMLISTDELINIT(list); + + sBuf = slab->buffers; + for (i=0; i < slab->numBuffers; ++i) { + if (sBuf->parent == slab) { + WSBM_COND_FREE(&sBuf->event); + wsbmBufStorageTakedown(&sBuf->storage); + } + sBuf++; + } + wsbmSetKernelBOFree(cache, slab->kbo); + free(slab->buffers); + free(slab); + } + } +} + +static void +wsbmSlabCheckFreeLocked(struct _WsbmSlabSizeHeader *header, int wait) +{ + struct _WsbmListHead *list, *prev, *first; + struct _WsbmSlabBuffer *sBuf; + struct _WsbmSlab *slab; + int firstWasSignaled = 1; + int signaled; + int i; + int ret; + + /* + * Rerun the freeing test if the youngest tested buffer + * was signaled, since there might be more idle buffers + * in the delay list. + */ + + while (firstWasSignaled) { + firstWasSignaled = 0; + signaled = 0; + first = header->delayedBuffers.next; + + /* Only examine the oldest 1/3 of delayed buffers: + */ + if (header->numDelayed > 3) { + for (i = 0; i < header->numDelayed; i += 3) { + first = first->next; + } + } + + /* + * No need to take the buffer mutex for each buffer we loop + * through since we're currently the only user. + */ + + + WSBMLISTFOREACHPREVSAFE(list, prev, first->next) { + + if (list == &header->delayedBuffers) + break; + + sBuf = WSBMLISTENTRY(list, struct _WsbmSlabBuffer, head); + slab = sBuf->parent; + + if (!signaled) { + if (wait) { + ret = wsbmFenceFinish(sBuf->fence, sBuf->fenceType, 0); + if (ret) + break; + signaled = 1; + wait = 0; + } else { + signaled = wsbmFenceSignaled(sBuf->fence, sBuf->fenceType); +#ifdef DEBUG_FENCESIGNALED + fencesignaled++; +#endif + } + if (signaled) { + if (list == first) + firstWasSignaled = 1; + wsbmFenceUnReference(&sBuf->fence); + header->numDelayed--; + wsbmSlabFreeBufferLocked(sBuf); + } else + break; + } else if (wsbmFenceSignaledCached(sBuf->fence, sBuf->fenceType)) { + wsbmFenceUnReference(&sBuf->fence); + header->numDelayed--; + wsbmSlabFreeBufferLocked(sBuf); + } + } + } +} + + +static struct _WsbmSlabBuffer * +wsbmSlabAllocBuffer(struct _WsbmSlabSizeHeader *header) +{ + static struct _WsbmSlabBuffer *buf; + struct _WsbmSlab *slab; + struct _WsbmListHead *list; + int count = WSBM_SLABPOOL_ALLOC_RETRIES; + + WSBM_MUTEX_LOCK(&header->mutex); + while(header->slabs.next == &header->slabs && count > 0) { + wsbmSlabCheckFreeLocked(header, 0); + if (header->slabs.next != &header->slabs) + break; + + WSBM_MUTEX_UNLOCK(&header->mutex); + if (count != WSBM_SLABPOOL_ALLOC_RETRIES) + usleep(1000); + WSBM_MUTEX_LOCK(&header->mutex); + (void) wsbmAllocSlab(header); + count--; + } + + list = header->slabs.next; + if (list == &header->slabs) { + WSBM_MUTEX_UNLOCK(&header->mutex); + return NULL; + } + slab = WSBMLISTENTRY(list, struct _WsbmSlab, head); + if (--slab->numFree == 0) + WSBMLISTDELINIT(list); + + list = slab->freeBuffers.next; + WSBMLISTDELINIT(list); + + WSBM_MUTEX_UNLOCK(&header->mutex); + buf = WSBMLISTENTRY(list, struct _WsbmSlabBuffer, head); + +#ifdef DEBUG_FENCESIGNALED + createbuffer++; + if ((createbuffer % 1000) == 0) + _mesa_printf("Createbuffer %d fencesignaled %d\n", + createbuffer, fencesignaled); +#endif + return buf; +} + +static struct _WsbmBufStorage * +pool_create(struct _WsbmBufferPool *pool, unsigned long size, + uint32_t placement, unsigned alignment) +{ + struct _WsbmSlabPool *slabPool = slabPoolFromPool(pool); + struct _WsbmSlabSizeHeader *header; + struct _WsbmSlabBuffer *sBuf; + int i; + int ret; + + /* + * FIXME: Check for compatibility. + */ + + header = slabPool->headers; + for (i=0; i<slabPool->numBuckets; ++i) { + if (header->bufSize >= size) + break; + header++; + } + + if (i < slabPool->numBuckets) { + sBuf = wsbmSlabAllocBuffer(header); + return ((sBuf) ? &sBuf->storage : NULL); + } + + + /* + * Fall back to allocate a buffer object directly from DRM. + * and wrap it in a wsbmBO structure. + */ + + sBuf = calloc(1, sizeof(*sBuf)); + + if (!sBuf) + return NULL; + + if (alignment) { + if ((alignment < slabPool->pageSize) && (slabPool->pageSize % alignment)) + goto out_err0; + if ((alignment > slabPool->pageSize) && (alignment % slabPool->pageSize)) + goto out_err0; + } + + ret = wsbmBufStorageInit(&sBuf->storage, &header->slabPool->pool); + if (ret) + goto out_err0; + + ret = WSBM_COND_INIT(&sBuf->event); + if (ret) + goto out_err1; + + { + union ttm_pl_create_arg arg; + + arg.req.size = size; + arg.req.placement = placement; + arg.req.page_alignment = alignment / slabPool->pageSize; + + DRMRESTARTCOMMANDWRITEREAD(pool->fd, + slabPool->devOffset + TTM_PL_CREATE, + arg, ret); + + if (ret) + goto out_err2; + + sBuf->kBuf.gpuOffset = arg.rep.gpu_offset; + sBuf->kBuf.placement = arg.rep.placement; + sBuf->kBuf.handle = arg.rep.handle; + sBuf->mapHandle = arg.rep.map_handle; + sBuf->requestedSize = size; + + sBuf->virtual = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, + pool->fd, sBuf->mapHandle); + + if (sBuf->virtual == MAP_FAILED) + goto out_err3; + + } + + wsbmAtomicSet(&sBuf->writers, 0); + return &sBuf->storage; + out_err3: + { + struct ttm_pl_reference_req arg; + + arg.handle = sBuf->kBuf.handle; + (void) drmCommandWriteRead(pool->fd, + slabPool->devOffset + TTM_PL_UNREF, + &arg, sizeof(arg)); + } + out_err2: + WSBM_COND_FREE(&sBuf->event); + out_err1: + wsbmBufStorageTakedown(&sBuf->storage); + out_err0: + free(sBuf); + return NULL; +} + +static void +pool_destroy(struct _WsbmBufStorage **p_buf) +{ + struct _WsbmBufStorage *buf = *p_buf; + struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); + struct _WsbmSlab *slab; + struct _WsbmSlabSizeHeader *header; + + *p_buf = NULL; + + if (!sBuf->isSlabBuffer) { + struct _WsbmSlabPool *slabPool = slabPoolFromBuf(sBuf); + struct ttm_pl_reference_req arg; + + if (sBuf->virtual != NULL) { + (void) munmap(sBuf->virtual, sBuf->requestedSize); + sBuf->virtual = NULL; + } + + arg.handle = sBuf->kBuf.handle; + (void) drmCommandWrite(slabPool->pool.fd, + slabPool->devOffset + TTM_PL_UNREF, + &arg, sizeof(arg)); + + WSBM_COND_FREE(&sBuf->event); + wsbmBufStorageTakedown(&sBuf->storage); + free(sBuf); + return; + } + + slab = sBuf->parent; + header = slab->header; + + /* + * No need to take the buffer mutex below since we're the only user. + */ + + WSBM_MUTEX_LOCK(&header->mutex); + sBuf->unFenced = 0; + wsbmAtomicSet(&sBuf->writers, 0); + wsbmAtomicSet(&sBuf->storage.refCount, 1); + + if (sBuf->fence && !wsbmFenceSignaledCached(sBuf->fence, sBuf->fenceType)) { + WSBMLISTADDTAIL(&sBuf->head, &header->delayedBuffers); + header->numDelayed++; + } else { + if (sBuf->fence) + wsbmFenceUnReference(&sBuf->fence); + wsbmSlabFreeBufferLocked(sBuf); + } + + WSBM_MUTEX_UNLOCK(&header->mutex); +} + + +static void +waitIdleLocked(struct _WsbmSlabBuffer *sBuf, int lazy) +{ + struct _WsbmBufStorage *storage = &sBuf->storage; + + while(sBuf->unFenced || sBuf->fence != NULL) { + + if (sBuf->unFenced) + WSBM_COND_WAIT(&sBuf->event, &storage->mutex); + + if (sBuf->fence != NULL) { + if (!wsbmFenceSignaled(sBuf->fence, sBuf->fenceType)) { + struct _WsbmFenceObject *fence = + wsbmFenceReference(sBuf->fence); + + WSBM_MUTEX_UNLOCK(&storage->mutex); + (void) wsbmFenceFinish(fence, sBuf->fenceType, lazy); + WSBM_MUTEX_LOCK(&storage->mutex); + if (sBuf->fence == fence) + wsbmFenceUnReference(&sBuf->fence); + + wsbmFenceUnReference(&fence); + } else { + wsbmFenceUnReference(&sBuf->fence); + } + } + } +} + +static int +pool_waitIdle(struct _WsbmBufStorage *buf, int lazy) +{ + struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); + + WSBM_MUTEX_LOCK(&buf->mutex); + waitIdleLocked(sBuf, lazy); + WSBM_MUTEX_UNLOCK(&buf->mutex); + + return 0; +} + +static int +pool_map(struct _WsbmBufStorage *buf, unsigned mode, void **virtual) +{ + struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); + + *virtual = sBuf->virtual; + + return 0; +} + +static void +pool_releaseFromCpu(struct _WsbmBufStorage *buf, unsigned mode) +{ + struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); + + if (wsbmAtomicDecZero(&sBuf->writers)) + WSBM_COND_BROADCAST(&sBuf->event); +} + +static int +pool_syncForCpu(struct _WsbmBufStorage *buf, unsigned mode) +{ + struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); + int ret = 0; + + WSBM_MUTEX_LOCK(&buf->mutex); + if ((mode & WSBM_SYNCCPU_DONT_BLOCK)) { + int signaled; + + if (sBuf->unFenced) { + ret = -EBUSY; + goto out_unlock; + } + + if (sBuf->isSlabBuffer) + signaled = (sBuf->fence == NULL) || + wsbmFenceSignaledCached(sBuf->fence, sBuf->fenceType); + else + signaled = (sBuf->fence == NULL) || + wsbmFenceSignaled(sBuf->fence, sBuf->fenceType); + + ret = 0; + if (signaled) { + wsbmFenceUnReference(&sBuf->fence); + wsbmAtomicInc(&sBuf->writers); + } else + ret = -EBUSY; + goto out_unlock; + } + waitIdleLocked(sBuf, 0); + wsbmAtomicInc(&sBuf->writers); + out_unlock: + WSBM_MUTEX_UNLOCK(&buf->mutex); + return ret; +} + +static void +pool_unmap(struct _WsbmBufStorage *buf) +{ + ; +} + +static unsigned long +pool_poolOffset(struct _WsbmBufStorage *buf) +{ + struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); + + return sBuf->start; +} + +static unsigned long +pool_size(struct _WsbmBufStorage *buf) +{ + struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); + if (!sBuf->isSlabBuffer) + return sBuf->requestedSize; + + return sBuf->parent->header->bufSize; +} + +static struct _WsbmKernelBuf * +pool_kernel(struct _WsbmBufStorage *buf) +{ + struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); + + return (sBuf->isSlabBuffer) ? &sBuf->parent->kbo->kBuf : &sBuf->kBuf; +} + + +static unsigned long +pool_offset(struct _WsbmBufStorage *buf) +{ + return pool_kernel(buf)->gpuOffset + pool_poolOffset(buf); +} + + +static void +pool_fence(struct _WsbmBufStorage *buf, struct _WsbmFenceObject *fence) +{ + struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); + struct _WsbmKernelBuf *kBuf; + + WSBM_MUTEX_LOCK(&buf->mutex); + if (sBuf->fence) + wsbmFenceUnReference(&sBuf->fence); + + sBuf->fence = wsbmFenceReference(fence); + kBuf = pool_kernel(buf); + sBuf->fenceType = kBuf->fence_type_mask; + sBuf->unFenced = 0; + WSBM_COND_BROADCAST(&sBuf->event); + WSBM_MUTEX_UNLOCK(&buf->mutex); +} + +static int +pool_validate(struct _WsbmBufStorage *buf, + uint64_t set_flags, + uint64_t clr_flags) +{ + struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); + + WSBM_MUTEX_LOCK(&buf->mutex); + while(wsbmAtomicRead(&sBuf->writers) != 0) { + WSBM_COND_WAIT(&sBuf->event, &buf->mutex); + } + + sBuf->unFenced = 1; + WSBM_MUTEX_UNLOCK(&buf->mutex); + return 0; +} + +static void +pool_unvalidate(struct _WsbmBufStorage *buf) +{ + struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); + + WSBM_MUTEX_LOCK(&buf->mutex); + if (sBuf->unFenced) { + sBuf->unFenced = 0; + WSBM_COND_BROADCAST(&sBuf->event); + } + WSBM_MUTEX_UNLOCK(&buf->mutex); +} + +struct _WsbmSlabCache * +wsbmSlabCacheInit(uint32_t checkIntervalMsec, uint32_t slabTimeoutMsec) +{ + struct _WsbmSlabCache *tmp; + + tmp = calloc(1, sizeof(*tmp)); + if (!tmp) + return NULL; + + WSBM_MUTEX_INIT(&tmp->mutex); + WSBM_MUTEX_LOCK(&tmp->mutex); + tmp->slabTimeout.tv_usec = slabTimeoutMsec*1000; + tmp->slabTimeout.tv_sec = tmp->slabTimeout.tv_usec / 1000000; + tmp->slabTimeout.tv_usec -= tmp->slabTimeout.tv_sec*1000000; + + tmp->checkInterval.tv_usec = checkIntervalMsec*1000; + tmp->checkInterval.tv_sec = tmp->checkInterval.tv_usec / 1000000; + tmp->checkInterval.tv_usec -= tmp->checkInterval.tv_sec*1000000; + + gettimeofday(&tmp->nextCheck, NULL); + wsbmTimeAdd(&tmp->nextCheck, &tmp->checkInterval); + WSBMINITLISTHEAD(&tmp->timeoutList); + WSBMINITLISTHEAD(&tmp->unCached); + WSBMINITLISTHEAD(&tmp->cached); + WSBM_MUTEX_UNLOCK(&tmp->mutex); + + return tmp; +} + +void +wsbmSlabCacheFinish(struct _WsbmSlabCache *cache) +{ + struct timeval time; + + time = cache->nextCheck; + WSBM_MUTEX_LOCK(&cache->mutex); + wsbmTimeAdd(&time, &cache->checkInterval); + wsbmFreeTimeoutKBOsLocked(cache, &time); + WSBM_MUTEX_UNLOCK(&cache->mutex); + + assert(cache->timeoutList.next == &cache->timeoutList); + assert(cache->unCached.next == &cache->unCached); + assert(cache->cached.next == &cache->cached); + + WSBM_MUTEX_FREE(&cache->mutex); + free(cache); +} + +static void +wsbmInitSizeHeader(struct _WsbmSlabPool *slabPool, uint32_t size, + struct _WsbmSlabSizeHeader *header) +{ + WSBM_MUTEX_INIT(&header->mutex); + WSBM_MUTEX_LOCK(&header->mutex); + + WSBMINITLISTHEAD(&header->slabs); + WSBMINITLISTHEAD(&header->freeSlabs); + WSBMINITLISTHEAD(&header->delayedBuffers); + + header->numDelayed = 0; + header->slabPool = slabPool; + header->bufSize = size; + + WSBM_MUTEX_UNLOCK(&header->mutex); +} + +static void +wsbmFinishSizeHeader(struct _WsbmSlabSizeHeader *header) +{ + struct _WsbmListHead *list, *next; + struct _WsbmSlabBuffer *sBuf; + + WSBM_MUTEX_LOCK(&header->mutex); + WSBMLISTFOREACHSAFE(list, next, &header->delayedBuffers) { + sBuf = WSBMLISTENTRY(list, struct _WsbmSlabBuffer, head); + if (sBuf->fence) { + (void) wsbmFenceFinish(sBuf->fence, sBuf->fenceType, 0); + wsbmFenceUnReference(&sBuf->fence); + } + header->numDelayed--; + wsbmSlabFreeBufferLocked(sBuf); + } + WSBM_MUTEX_UNLOCK(&header->mutex); + WSBM_MUTEX_FREE(&header->mutex); +} + + +static void +pool_takedown(struct _WsbmBufferPool *pool) +{ + struct _WsbmSlabPool *slabPool = slabPoolFromPool(pool); + int i; + + for (i=0; i<slabPool->numBuckets; ++i) { + wsbmFinishSizeHeader(&slabPool->headers[i]); + } + + free(slabPool->headers); + free(slabPool->bucketSizes); + free(slabPool); +} + +struct _WsbmBufferPool * +wsbmSlabPoolInit(int fd, + uint32_t devOffset, + uint32_t placement, + uint32_t validMask, + uint32_t smallestSize, + uint32_t numSizes, + uint32_t desiredNumBuffers, + uint32_t maxSlabSize, + uint32_t pageAlignment, + struct _WsbmSlabCache *cache) +{ + struct _WsbmBufferPool *pool; + struct _WsbmSlabPool *slabPool; + uint32_t i; + + slabPool = calloc(1, sizeof(*slabPool)); + if (!slabPool) + return NULL; + + pool = &slabPool->pool; + + slabPool->bucketSizes = calloc(numSizes, sizeof(*slabPool->bucketSizes)); + if (!slabPool->bucketSizes) + goto out_err0; + + slabPool->headers = calloc(numSizes, sizeof(*slabPool->headers)); + if (!slabPool->headers) + goto out_err1; + + slabPool->devOffset = devOffset; + slabPool->cache = cache; + slabPool->proposedPlacement = placement; + slabPool->validMask = validMask; + slabPool->numBuckets = numSizes; + slabPool->pageSize = getpagesize(); + slabPool->pageAlignment = pageAlignment; + slabPool->maxSlabSize = maxSlabSize; + slabPool->desiredNumBuffers = desiredNumBuffers; + + for (i=0; i<slabPool->numBuckets; ++i) { + slabPool->bucketSizes[i] = (smallestSize << i); + wsbmInitSizeHeader(slabPool, slabPool->bucketSizes[i], + &slabPool->headers[i]); + } + + pool->fd = fd; + pool->map = &pool_map; + pool->unmap = &pool_unmap; + pool->destroy = &pool_destroy; + pool->offset = &pool_offset; + pool->poolOffset = &pool_poolOffset; + pool->size = &pool_size; + pool->create = &pool_create; + pool->fence = &pool_fence; + pool->kernel = &pool_kernel; + pool->validate = &pool_validate; + pool->unvalidate = &pool_unvalidate; + pool->waitIdle = &pool_waitIdle; + pool->takeDown = &pool_takedown; + pool->releasefromcpu = &pool_releaseFromCpu; + pool->syncforcpu = &pool_syncForCpu; + + return pool; + + out_err1: + free(slabPool->bucketSizes); + out_err0: + free(slabPool); + + return NULL; +} |