/************************************************************************** * * Copyright 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 "wsbm_fencemgr.h" #include "wsbm_pool.h" #include "wsbm_manager.h" #include <xf86drm.h> #include <drm/psb_ttm_fence_user.h> #include <string.h> #include <unistd.h> struct _WsbmFenceClass { struct _WsbmListHead head; struct _WsbmMutex mutex; struct _WsbmMutex cmd_mutex; }; /* * Note: The struct _WsbmFenceMgr::Mutex should never be held * during sleeps, since that may block fast concurrent access to * fence data. */ struct _WsbmFenceMgr { /* * Constant members. Need no mutex protection. */ struct _WsbmFenceMgrCreateInfo info; void *private; /* * Atomic members. No mutex protection. */ struct _WsbmAtomic count; /* * These members are protected by this->mutex */ struct _WsbmFenceClass *classes; uint32_t num_classes; }; struct _WsbmFenceObject { /* * These members are constant and need no mutex protection. * Note that @private may point to a structure with its own * mutex protection, that we don't care about. */ struct _WsbmFenceMgr *mgr; uint32_t fence_class; uint32_t fence_type; void *private; /* * Atomic members. No mutex protection. note that * @signaled types is updated using a compare-and-swap * scheme to guarantee atomicity. */ struct _WsbmAtomic refCount; struct _WsbmAtomic signaled_types; /* * These members are protected by mgr->mutex. */ struct _WsbmListHead head; }; uint32_t wsbmFenceType(struct _WsbmFenceObject *fence) { return fence->fence_type; } struct _WsbmFenceMgr * wsbmFenceMgrCreate(const struct _WsbmFenceMgrCreateInfo *info) { struct _WsbmFenceMgr *tmp; uint32_t i, j; int ret; tmp = calloc(1, sizeof(*tmp)); if (!tmp) return NULL; tmp->info = *info; tmp->classes = calloc(tmp->info.num_classes, sizeof(*tmp->classes)); if (!tmp->classes) goto out_err; for (i = 0; i < tmp->info.num_classes; ++i) { struct _WsbmFenceClass *fc = &tmp->classes[i]; WSBMINITLISTHEAD(&fc->head); ret = WSBM_MUTEX_INIT(&fc->mutex); if (ret) goto out_err1; ret = WSBM_MUTEX_INIT(&fc->cmd_mutex); if (ret) { WSBM_MUTEX_FREE(&fc->mutex); goto out_err1; } } wsbmAtomicSet(&tmp->count, 0); return tmp; out_err1: for (j = 0; j < i; ++j) { WSBM_MUTEX_FREE(&tmp->classes[j].mutex); WSBM_MUTEX_FREE(&tmp->classes[j].cmd_mutex); } free(tmp->classes); out_err: if (tmp) free(tmp); return NULL; } void wsbmFenceUnreference(struct _WsbmFenceObject **pFence) { struct _WsbmFenceObject *fence = *pFence; struct _WsbmFenceMgr *mgr; *pFence = NULL; if (fence == NULL) return; mgr = fence->mgr; if (wsbmAtomicDecZero(&fence->refCount)) { struct _WsbmFenceClass *fc = &mgr->classes[fence->fence_class]; WSBM_MUTEX_LOCK(&fc->mutex); WSBMLISTDELINIT(&fence->head); WSBM_MUTEX_UNLOCK(&fc->mutex); if (fence->private) mgr->info.unreference(mgr, &fence->private); fence->mgr = NULL; wsbmAtomicDecZero(&mgr->count); free(fence); } } static void wsbmSignalPreviousFences(struct _WsbmFenceMgr *mgr, struct _WsbmListHead *list, uint32_t fence_class, uint32_t signaled_types) { struct _WsbmFenceClass *fc = &mgr->classes[fence_class]; struct _WsbmFenceObject *entry; struct _WsbmListHead *prev; uint32_t old_signaled_types; uint32_t ret_st; WSBM_MUTEX_LOCK(&fc->mutex); while (list != &fc->head && list->next != list) { entry = WSBMLISTENTRY(list, struct _WsbmFenceObject, head); prev = list->prev; do { old_signaled_types = wsbmAtomicRead(&entry->signaled_types); signaled_types = old_signaled_types | (signaled_types & entry->fence_type); if (signaled_types == old_signaled_types) break; ret_st = wsbmAtomicCmpXchg(&entry->signaled_types, old_signaled_types, signaled_types); } while (ret_st != old_signaled_types); if (signaled_types == entry->fence_type) WSBMLISTDELINIT(list); list = prev; } WSBM_MUTEX_UNLOCK(&fc->mutex); } int wsbmFenceFinish(struct _WsbmFenceObject *fence, uint32_t fence_type, int lazy_hint) { struct _WsbmFenceMgr *mgr = fence->mgr; int ret = 0; if ((wsbmAtomicRead(&fence->signaled_types) & fence_type) == fence_type) goto out; ret = mgr->info.finish(mgr, fence->private, fence_type, lazy_hint); if (ret) goto out; wsbmSignalPreviousFences(mgr, &fence->head, fence->fence_class, fence_type); out: return ret; } uint32_t wsbmFenceSignaledTypeCached(struct _WsbmFenceObject * fence) { return wsbmAtomicRead(&fence->signaled_types); } int wsbmFenceSignaledType(struct _WsbmFenceObject *fence, uint32_t flush_type, uint32_t * signaled) { int ret = 0; struct _WsbmFenceMgr *mgr; uint32_t signaled_types; uint32_t old_signaled_types; uint32_t ret_st; mgr = fence->mgr; *signaled = wsbmAtomicRead(&fence->signaled_types); if ((*signaled & flush_type) == flush_type) goto out0; ret = mgr->info.signaled(mgr, fence->private, flush_type, signaled); if (ret) { *signaled = wsbmAtomicRead(&fence->signaled_types); goto out0; } do { old_signaled_types = wsbmAtomicRead(&fence->signaled_types); signaled_types = old_signaled_types | *signaled; if (signaled_types == old_signaled_types) break; ret_st = wsbmAtomicCmpXchg(&fence->signaled_types, old_signaled_types, signaled_types); if (old_signaled_types == ret_st) wsbmSignalPreviousFences(mgr, &fence->head, fence->fence_class, *signaled); } while (old_signaled_types != ret_st); return 0; out0: return ret; } struct _WsbmFenceObject * wsbmFenceReference(struct _WsbmFenceObject *fence) { if (fence == NULL) return NULL; wsbmAtomicInc(&fence->refCount); return fence; } struct _WsbmFenceObject * wsbmFenceCreateSig(struct _WsbmFenceMgr *mgr, uint32_t fence_class, uint32_t fence_type, uint32_t signaled_types, void *private, size_t private_size) { struct _WsbmFenceClass *fc = &mgr->classes[fence_class]; struct _WsbmFenceObject *fence; size_t fence_size = sizeof(*fence); if (private_size) fence_size = ((fence_size + 15) & ~15); fence = calloc(1, fence_size + private_size); if (!fence) goto out_err; wsbmAtomicSet(&fence->refCount, 1); fence->mgr = mgr; fence->fence_class = fence_class; fence->fence_type = fence_type; wsbmAtomicSet(&fence->signaled_types, signaled_types); fence->private = private; if (private_size) { fence->private = (void *)(((uint8_t *) fence) + fence_size); memcpy(fence->private, private, private_size); } WSBM_MUTEX_LOCK(&fc->mutex); WSBMLISTADDTAIL(&fence->head, &fc->head); WSBM_MUTEX_UNLOCK(&fc->mutex); wsbmAtomicInc(&mgr->count); return fence; out_err: { int ret = mgr->info.finish(mgr, private, fence_type, 0); if (ret) usleep(10000000); } if (fence) free(fence); mgr->info.unreference(mgr, &private); return NULL; } struct _WsbmFenceObject * wsbmFenceCreate(struct _WsbmFenceMgr *mgr, uint32_t fence_class, uint32_t fence_type, void *private, size_t private_size) { return wsbmFenceCreateSig(mgr, fence_class, fence_type, 0, private, private_size); } struct _WsbmTTMFenceMgrPriv { int fd; unsigned int devOffset; }; static int tSignaled(struct _WsbmFenceMgr *mgr, void *private, uint32_t flush_type, uint32_t * signaled_type) { struct _WsbmTTMFenceMgrPriv *priv = (struct _WsbmTTMFenceMgrPriv *)mgr->private; union ttm_fence_signaled_arg arg; int ret; arg.req.handle = (unsigned long)private; arg.req.fence_type = flush_type; arg.req.flush = 1; *signaled_type = 0; ret = drmCommandWriteRead(priv->fd, priv->devOffset + TTM_FENCE_SIGNALED, &arg, sizeof(arg)); if (ret) return ret; *signaled_type = arg.rep.signaled_types; return 0; } static int tFinish(struct _WsbmFenceMgr *mgr, void *private, uint32_t fence_type, int lazy_hint) { struct _WsbmTTMFenceMgrPriv *priv = (struct _WsbmTTMFenceMgrPriv *)mgr->private; union ttm_fence_finish_arg arg = {.req = {.handle = (unsigned long)private, .fence_type = fence_type, .mode = (lazy_hint) ? TTM_FENCE_FINISH_MODE_LAZY : 0} }; int ret; do { ret = drmCommandWriteRead(priv->fd, priv->devOffset + TTM_FENCE_FINISH, &arg, sizeof(arg)); } while (ret == -EAGAIN || ret == -ERESTART); return ret; } static int tUnref(struct _WsbmFenceMgr *mgr, void **private) { struct _WsbmTTMFenceMgrPriv *priv = (struct _WsbmTTMFenceMgrPriv *)mgr->private; struct ttm_fence_unref_arg arg = {.handle = (unsigned long)*private }; *private = NULL; return drmCommandWrite(priv->fd, priv->devOffset + TTM_FENCE_UNREF, &arg, sizeof(arg)); } struct _WsbmFenceMgr * wsbmFenceMgrTTMInit(int fd, unsigned int numClass, unsigned int devOffset) { struct _WsbmFenceMgrCreateInfo info; struct _WsbmFenceMgr *mgr; struct _WsbmTTMFenceMgrPriv *priv = malloc(sizeof(*priv)); if (!priv) return NULL; priv->fd = fd; priv->devOffset = devOffset; info.flags = WSBM_FENCE_CLASS_ORDERED; info.num_classes = numClass; info.signaled = tSignaled; info.finish = tFinish; info.unreference = tUnref; mgr = wsbmFenceMgrCreate(&info); if (mgr == NULL) { free(priv); return NULL; } mgr->private = (void *)priv; return mgr; } void wsbmFenceCmdLock(struct _WsbmFenceMgr *mgr, uint32_t fence_class) { WSBM_MUTEX_LOCK(&mgr->classes[fence_class].cmd_mutex); } void wsbmFenceCmdUnlock(struct _WsbmFenceMgr *mgr, uint32_t fence_class) { WSBM_MUTEX_UNLOCK(&mgr->classes[fence_class].cmd_mutex); } void wsbmFenceMgrTTMTakedown(struct _WsbmFenceMgr *mgr) { unsigned int i; if (!mgr) return; if (mgr->private) free(mgr->private); for (i = 0; i < mgr->info.num_classes; ++i) { WSBM_MUTEX_FREE(&mgr->classes[i].mutex); WSBM_MUTEX_FREE(&mgr->classes[i].cmd_mutex); } free(mgr); return; }