/************************************************************************** * * Copyright 2006-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA * All Rights Reserved. * Copyright 2009 VMware, Inc., Palo Alto, CA., 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 above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * 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. * **************************************************************************/ /* * Authors: Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <drm/psb_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" #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; } static void wsbmFreeKernelBO(struct _WsbmSlabKernelBO *kbo) { struct ttm_pl_reference_req arg; struct _WsbmSlabPool *slabPool; if (!kbo) return; 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 <= (uint32_t) slabPool->maxSlabSize) ? size : (uint32_t) 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; } 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; uint32_t ret; uint32_t 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; if (!numBuffers) { ret = -ENOMEM; goto out_err1; } 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++; } free(slab->buffers); out_err1: wsbmSetKernelBOFree(header->slabPool->cache, slab->kbo); 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) { uint32_t 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, *head; struct _WsbmSlabBuffer *sBuf; struct _WsbmSlab *slab; int firstWasSignaled = 1; int signaled; uint32_t 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. */ head = first->next; WSBMLISTFOREACHPREVSAFE(list, prev, head) { 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); buf->storage.destroyContainer = NULL; #ifdef DEBUG_FENCESIGNALED createbuffer++; #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; uint32_t 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, 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 __attribute__ ((unused)), void **virtual) { struct _WsbmSlabBuffer *sBuf = slabBuffer(buf); *virtual = sBuf->virtual; return 0; } static void pool_releaseFromCpu(struct _WsbmBufStorage *buf, unsigned mode __attribute__ ((unused))) { 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 __attribute__ ((unused))) { ; } 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); kBuf = pool_kernel(buf); sBuf->fenceType = kBuf->fence_type_mask; if (!wsbmFenceSignaledCached(fence, sBuf->fenceType)) sBuf->fence = wsbmFenceReference(fence); sBuf->unFenced = 0; WSBM_COND_BROADCAST(&sBuf->event); WSBM_MUTEX_UNLOCK(&buf->mutex); } static int pool_validate(struct _WsbmBufStorage *buf, uint64_t set_flags __attribute__ ((unused)), uint64_t clr_flags __attribute__ ((unused))) { 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); unsigned 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; }