Path: blob/21.2-virgl/src/gallium/winsys/svga/drm/pb_buffer_simple_fenced.c
4573 views
/**************************************************************************1*2* Copyright 2007-2015 VMware, Inc.3* All Rights Reserved.4*5* Permission is hereby granted, free of charge, to any person obtaining a6* copy of this software and associated documentation files (the7* "Software"), to deal in the Software without restriction, including8* without limitation the rights to use, copy, modify, merge, publish,9* distribute, sub license, and/or sell copies of the Software, and to10* permit persons to whom the Software is furnished to do so, subject to11* the following conditions:12*13* The above copyright notice and this permission notice (including the14* next paragraph) shall be included in all copies or substantial portions15* of the Software.16*17* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS18* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF19* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.20* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR21* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,22* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE23* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.24*25**************************************************************************/2627/**28* \file29* Implementation of fenced buffers.30*31* \author Jose Fonseca <jfonseca-at-vmware-dot-com>32* \author Thomas Hellström <thellstrom-at-vmware-dot-com>33*/343536#include "pipe/p_config.h"3738#if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS)39#include <unistd.h>40#include <sched.h>41#endif42#include <inttypes.h>4344#include "pipe/p_compiler.h"45#include "pipe/p_defines.h"46#include "util/u_debug.h"47#include "os/os_thread.h"48#include "util/u_memory.h"49#include "util/list.h"5051#include "pipebuffer/pb_buffer.h"52#include "pipebuffer/pb_bufmgr.h"53#include "pipebuffer/pb_buffer_fenced.h"54#include "vmw_screen.h"555657/**58* Convenience macro (type safe).59*/60#define SUPER(__derived) (&(__derived)->base)616263struct fenced_manager64{65struct pb_manager base;66struct pb_manager *provider;67struct pb_fence_ops *ops;6869/**70* Following members are mutable and protected by this mutex.71*/72mtx_t mutex;7374/**75* Fenced buffer list.76*77* All fenced buffers are placed in this listed, ordered from the oldest78* fence to the newest fence.79*/80struct list_head fenced;81pb_size num_fenced;8283struct list_head unfenced;84pb_size num_unfenced;8586};878889/**90* Fenced buffer.91*92* Wrapper around a pipe buffer which adds fencing and reference counting.93*/94struct fenced_buffer95{96/*97* Immutable members.98*/99100struct pb_buffer base;101struct fenced_manager *mgr;102103/*104* Following members are mutable and protected by fenced_manager::mutex.105*/106107struct list_head head;108109/**110* Buffer with storage.111*/112struct pb_buffer *buffer;113pb_size size;114115/**116* A bitmask of PB_USAGE_CPU/GPU_READ/WRITE describing the current117* buffer usage.118*/119unsigned flags;120121unsigned mapcount;122123struct pb_validate *vl;124unsigned validation_flags;125126struct pipe_fence_handle *fence;127};128129130static inline struct fenced_manager *131fenced_manager(struct pb_manager *mgr)132{133assert(mgr);134return (struct fenced_manager *)mgr;135}136137138static inline struct fenced_buffer *139fenced_buffer(struct pb_buffer *buf)140{141assert(buf);142return (struct fenced_buffer *)buf;143}144145146static void147fenced_buffer_destroy_gpu_storage_locked(struct fenced_buffer *fenced_buf);148149static enum pipe_error150fenced_buffer_create_gpu_storage_locked(struct fenced_manager *fenced_mgr,151struct fenced_buffer *fenced_buf,152const struct pb_desc *desc,153boolean wait);154/**155* Dump the fenced buffer list.156*157* Useful to understand failures to allocate buffers.158*/159static void160fenced_manager_dump_locked(struct fenced_manager *fenced_mgr)161{162#ifdef DEBUG163struct pb_fence_ops *ops = fenced_mgr->ops;164struct list_head *curr, *next;165struct fenced_buffer *fenced_buf;166167debug_printf("%10s %7s %8s %7s %10s %s\n",168"buffer", "size", "refcount", "storage", "fence", "signalled");169170curr = fenced_mgr->unfenced.next;171next = curr->next;172while(curr != &fenced_mgr->unfenced) {173fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head);174assert(!fenced_buf->fence);175debug_printf("%10p %"PRIu64" %8u %7s\n",176(void *) fenced_buf,177fenced_buf->base.size,178p_atomic_read(&fenced_buf->base.reference.count),179fenced_buf->buffer ? "gpu" : "none");180curr = next;181next = curr->next;182}183184curr = fenced_mgr->fenced.next;185next = curr->next;186while(curr != &fenced_mgr->fenced) {187int signaled;188fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head);189assert(fenced_buf->buffer);190signaled = ops->fence_signalled(ops, fenced_buf->fence, 0);191debug_printf("%10p %"PRIu64" %8u %7s %10p %s\n",192(void *) fenced_buf,193fenced_buf->base.size,194p_atomic_read(&fenced_buf->base.reference.count),195"gpu",196(void *) fenced_buf->fence,197signaled == 0 ? "y" : "n");198curr = next;199next = curr->next;200}201#else202(void)fenced_mgr;203#endif204}205206207static inline void208fenced_buffer_destroy_locked(struct fenced_manager *fenced_mgr,209struct fenced_buffer *fenced_buf)210{211assert(!pipe_is_referenced(&fenced_buf->base.reference));212213assert(!fenced_buf->fence);214assert(fenced_buf->head.prev);215assert(fenced_buf->head.next);216list_del(&fenced_buf->head);217assert(fenced_mgr->num_unfenced);218--fenced_mgr->num_unfenced;219220fenced_buffer_destroy_gpu_storage_locked(fenced_buf);221222FREE(fenced_buf);223}224225226/**227* Add the buffer to the fenced list.228*229* Reference count should be incremented before calling this function.230*/231static inline void232fenced_buffer_add_locked(struct fenced_manager *fenced_mgr,233struct fenced_buffer *fenced_buf)234{235assert(pipe_is_referenced(&fenced_buf->base.reference));236assert(fenced_buf->flags & PB_USAGE_GPU_READ_WRITE);237assert(fenced_buf->fence);238239p_atomic_inc(&fenced_buf->base.reference.count);240241list_del(&fenced_buf->head);242assert(fenced_mgr->num_unfenced);243--fenced_mgr->num_unfenced;244list_addtail(&fenced_buf->head, &fenced_mgr->fenced);245++fenced_mgr->num_fenced;246}247248249/**250* Remove the buffer from the fenced list, and potentially destroy the buffer251* if the reference count reaches zero.252*253* Returns TRUE if the buffer was detroyed.254*/255static inline boolean256fenced_buffer_remove_locked(struct fenced_manager *fenced_mgr,257struct fenced_buffer *fenced_buf)258{259struct pb_fence_ops *ops = fenced_mgr->ops;260261assert(fenced_buf->fence);262assert(fenced_buf->mgr == fenced_mgr);263264ops->fence_reference(ops, &fenced_buf->fence, NULL);265fenced_buf->flags &= ~PB_USAGE_GPU_READ_WRITE;266267assert(fenced_buf->head.prev);268assert(fenced_buf->head.next);269270list_del(&fenced_buf->head);271assert(fenced_mgr->num_fenced);272--fenced_mgr->num_fenced;273274list_addtail(&fenced_buf->head, &fenced_mgr->unfenced);275++fenced_mgr->num_unfenced;276277if (p_atomic_dec_zero(&fenced_buf->base.reference.count)) {278fenced_buffer_destroy_locked(fenced_mgr, fenced_buf);279return TRUE;280}281282return FALSE;283}284285286/**287* Wait for the fence to expire, and remove it from the fenced list.288*289* This function will release and re-acquire the mutex, so any copy of mutable290* state must be discarded after calling it.291*/292static inline enum pipe_error293fenced_buffer_finish_locked(struct fenced_manager *fenced_mgr,294struct fenced_buffer *fenced_buf)295{296struct pb_fence_ops *ops = fenced_mgr->ops;297enum pipe_error ret = PIPE_ERROR;298299#if 0300debug_warning("waiting for GPU");301#endif302303assert(pipe_is_referenced(&fenced_buf->base.reference));304assert(fenced_buf->fence);305306if(fenced_buf->fence) {307struct pipe_fence_handle *fence = NULL;308int finished;309boolean proceed;310311ops->fence_reference(ops, &fence, fenced_buf->fence);312313mtx_unlock(&fenced_mgr->mutex);314315finished = ops->fence_finish(ops, fenced_buf->fence, 0);316317mtx_lock(&fenced_mgr->mutex);318319assert(pipe_is_referenced(&fenced_buf->base.reference));320321/*322* Only proceed if the fence object didn't change in the meanwhile.323* Otherwise assume the work has been already carried out by another324* thread that re-acquired the lock before us.325*/326proceed = fence == fenced_buf->fence ? TRUE : FALSE;327328ops->fence_reference(ops, &fence, NULL);329330if(proceed && finished == 0) {331/*332* Remove from the fenced list333*/334335boolean destroyed;336337destroyed = fenced_buffer_remove_locked(fenced_mgr, fenced_buf);338339/* TODO: remove consequents buffers with the same fence? */340341assert(!destroyed);342(void) destroyed;343344fenced_buf->flags &= ~PB_USAGE_GPU_READ_WRITE;345346ret = PIPE_OK;347}348}349350return ret;351}352353354/**355* Remove as many fenced buffers from the fenced list as possible.356*357* Returns TRUE if at least one buffer was removed.358*/359static boolean360fenced_manager_check_signalled_locked(struct fenced_manager *fenced_mgr,361boolean wait)362{363struct pb_fence_ops *ops = fenced_mgr->ops;364struct list_head *curr, *next;365struct fenced_buffer *fenced_buf;366struct pipe_fence_handle *prev_fence = NULL;367boolean ret = FALSE;368369curr = fenced_mgr->fenced.next;370next = curr->next;371while(curr != &fenced_mgr->fenced) {372fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head);373374if(fenced_buf->fence != prev_fence) {375int signaled;376377if (wait) {378signaled = ops->fence_finish(ops, fenced_buf->fence, 0);379380/*381* Don't return just now. Instead preemptively check if the382* following buffers' fences already expired,383* without further waits.384*/385wait = FALSE;386}387else {388signaled = ops->fence_signalled(ops, fenced_buf->fence, 0);389}390391if (signaled != 0) {392return ret;393}394395prev_fence = fenced_buf->fence;396}397else {398/* This buffer's fence object is identical to the previous buffer's399* fence object, so no need to check the fence again.400*/401assert(ops->fence_signalled(ops, fenced_buf->fence, 0) == 0);402}403404fenced_buffer_remove_locked(fenced_mgr, fenced_buf);405406ret = TRUE;407408curr = next;409next = curr->next;410}411412return ret;413}414415416/**417* Destroy the GPU storage.418*/419static void420fenced_buffer_destroy_gpu_storage_locked(struct fenced_buffer *fenced_buf)421{422if(fenced_buf->buffer) {423pb_reference(&fenced_buf->buffer, NULL);424}425}426427428/**429* Try to create GPU storage for this buffer.430*431* This function is a shorthand around pb_manager::create_buffer for432* fenced_buffer_create_gpu_storage_locked()'s benefit.433*/434static inline boolean435fenced_buffer_try_create_gpu_storage_locked(struct fenced_manager *fenced_mgr,436struct fenced_buffer *fenced_buf,437const struct pb_desc *desc)438{439struct pb_manager *provider = fenced_mgr->provider;440441assert(!fenced_buf->buffer);442443fenced_buf->buffer = provider->create_buffer(fenced_mgr->provider,444fenced_buf->size, desc);445return fenced_buf->buffer ? TRUE : FALSE;446}447448449/**450* Create GPU storage for this buffer.451*/452static enum pipe_error453fenced_buffer_create_gpu_storage_locked(struct fenced_manager *fenced_mgr,454struct fenced_buffer *fenced_buf,455const struct pb_desc *desc,456boolean wait)457{458assert(!fenced_buf->buffer);459460/*461* Check for signaled buffers before trying to allocate.462*/463fenced_manager_check_signalled_locked(fenced_mgr, FALSE);464465fenced_buffer_try_create_gpu_storage_locked(fenced_mgr, fenced_buf, desc);466467/*468* Keep trying while there is some sort of progress:469* - fences are expiring,470* - or buffers are being being swapped out from GPU memory into CPU memory.471*/472while(!fenced_buf->buffer &&473(fenced_manager_check_signalled_locked(fenced_mgr, FALSE))) {474fenced_buffer_try_create_gpu_storage_locked(fenced_mgr, fenced_buf,475desc);476}477478if(!fenced_buf->buffer && wait) {479/*480* Same as before, but this time around, wait to free buffers if481* necessary.482*/483while(!fenced_buf->buffer &&484(fenced_manager_check_signalled_locked(fenced_mgr, TRUE))) {485fenced_buffer_try_create_gpu_storage_locked(fenced_mgr, fenced_buf,486desc);487}488}489490if(!fenced_buf->buffer) {491if(0)492fenced_manager_dump_locked(fenced_mgr);493494/* give up */495return PIPE_ERROR_OUT_OF_MEMORY;496}497498return PIPE_OK;499}500501502static void503fenced_buffer_destroy(void *winsys, struct pb_buffer *buf)504{505struct fenced_buffer *fenced_buf = fenced_buffer(buf);506struct fenced_manager *fenced_mgr = fenced_buf->mgr;507508assert(!pipe_is_referenced(&fenced_buf->base.reference));509510mtx_lock(&fenced_mgr->mutex);511512fenced_buffer_destroy_locked(fenced_mgr, fenced_buf);513514mtx_unlock(&fenced_mgr->mutex);515}516517518static void *519fenced_buffer_map(struct pb_buffer *buf,520unsigned flags, void *flush_ctx)521{522struct fenced_buffer *fenced_buf = fenced_buffer(buf);523struct fenced_manager *fenced_mgr = fenced_buf->mgr;524struct pb_fence_ops *ops = fenced_mgr->ops;525void *map = NULL;526527mtx_lock(&fenced_mgr->mutex);528529assert(!(flags & PB_USAGE_GPU_READ_WRITE));530531/*532* Serialize writes.533*/534while((fenced_buf->flags & PB_USAGE_GPU_WRITE) ||535((fenced_buf->flags & PB_USAGE_GPU_READ) &&536(flags & PB_USAGE_CPU_WRITE))) {537538/*539* Don't wait for the GPU to finish accessing it,540* if blocking is forbidden.541*/542if((flags & PB_USAGE_DONTBLOCK) &&543ops->fence_signalled(ops, fenced_buf->fence, 0) != 0) {544goto done;545}546547if (flags & PB_USAGE_UNSYNCHRONIZED) {548break;549}550551/*552* Wait for the GPU to finish accessing. This will release and re-acquire553* the mutex, so all copies of mutable state must be discarded.554*/555fenced_buffer_finish_locked(fenced_mgr, fenced_buf);556}557558map = pb_map(fenced_buf->buffer, flags, flush_ctx);559560if(map) {561++fenced_buf->mapcount;562fenced_buf->flags |= flags & PB_USAGE_CPU_READ_WRITE;563}564565done:566mtx_unlock(&fenced_mgr->mutex);567568return map;569}570571572static void573fenced_buffer_unmap(struct pb_buffer *buf)574{575struct fenced_buffer *fenced_buf = fenced_buffer(buf);576struct fenced_manager *fenced_mgr = fenced_buf->mgr;577578mtx_lock(&fenced_mgr->mutex);579580assert(fenced_buf->mapcount);581if(fenced_buf->mapcount) {582if (fenced_buf->buffer)583pb_unmap(fenced_buf->buffer);584--fenced_buf->mapcount;585if(!fenced_buf->mapcount)586fenced_buf->flags &= ~PB_USAGE_CPU_READ_WRITE;587}588589mtx_unlock(&fenced_mgr->mutex);590}591592593static enum pipe_error594fenced_buffer_validate(struct pb_buffer *buf,595struct pb_validate *vl,596unsigned flags)597{598struct fenced_buffer *fenced_buf = fenced_buffer(buf);599struct fenced_manager *fenced_mgr = fenced_buf->mgr;600enum pipe_error ret;601602mtx_lock(&fenced_mgr->mutex);603604if(!vl) {605/* invalidate */606fenced_buf->vl = NULL;607fenced_buf->validation_flags = 0;608ret = PIPE_OK;609goto done;610}611612assert(flags & PB_USAGE_GPU_READ_WRITE);613assert(!(flags & ~PB_USAGE_GPU_READ_WRITE));614flags &= PB_USAGE_GPU_READ_WRITE;615616/* Buffer cannot be validated in two different lists */617if(fenced_buf->vl && fenced_buf->vl != vl) {618ret = PIPE_ERROR_RETRY;619goto done;620}621622if(fenced_buf->vl == vl &&623(fenced_buf->validation_flags & flags) == flags) {624/* Nothing to do -- buffer already validated */625ret = PIPE_OK;626goto done;627}628629ret = pb_validate(fenced_buf->buffer, vl, flags);630if (ret != PIPE_OK)631goto done;632633fenced_buf->vl = vl;634fenced_buf->validation_flags |= flags;635636done:637mtx_unlock(&fenced_mgr->mutex);638639return ret;640}641642643static void644fenced_buffer_fence(struct pb_buffer *buf,645struct pipe_fence_handle *fence)646{647struct fenced_buffer *fenced_buf = fenced_buffer(buf);648struct fenced_manager *fenced_mgr = fenced_buf->mgr;649struct pb_fence_ops *ops = fenced_mgr->ops;650651mtx_lock(&fenced_mgr->mutex);652653assert(pipe_is_referenced(&fenced_buf->base.reference));654assert(fenced_buf->buffer);655656if(fence != fenced_buf->fence) {657assert(fenced_buf->vl);658assert(fenced_buf->validation_flags);659660if (fenced_buf->fence) {661boolean destroyed;662destroyed = fenced_buffer_remove_locked(fenced_mgr, fenced_buf);663assert(!destroyed);664(void) destroyed;665}666if (fence) {667ops->fence_reference(ops, &fenced_buf->fence, fence);668fenced_buf->flags |= fenced_buf->validation_flags;669fenced_buffer_add_locked(fenced_mgr, fenced_buf);670}671672pb_fence(fenced_buf->buffer, fence);673674fenced_buf->vl = NULL;675fenced_buf->validation_flags = 0;676}677678mtx_unlock(&fenced_mgr->mutex);679}680681682static void683fenced_buffer_get_base_buffer(struct pb_buffer *buf,684struct pb_buffer **base_buf,685pb_size *offset)686{687struct fenced_buffer *fenced_buf = fenced_buffer(buf);688struct fenced_manager *fenced_mgr = fenced_buf->mgr;689690mtx_lock(&fenced_mgr->mutex);691692assert(fenced_buf->buffer);693694if(fenced_buf->buffer)695pb_get_base_buffer(fenced_buf->buffer, base_buf, offset);696else {697*base_buf = buf;698*offset = 0;699}700701mtx_unlock(&fenced_mgr->mutex);702}703704705static const struct pb_vtbl706fenced_buffer_vtbl = {707fenced_buffer_destroy,708fenced_buffer_map,709fenced_buffer_unmap,710fenced_buffer_validate,711fenced_buffer_fence,712fenced_buffer_get_base_buffer713};714715716/**717* Wrap a buffer in a fenced buffer.718*/719static struct pb_buffer *720fenced_bufmgr_create_buffer(struct pb_manager *mgr,721pb_size size,722const struct pb_desc *desc)723{724struct fenced_manager *fenced_mgr = fenced_manager(mgr);725struct fenced_buffer *fenced_buf;726enum pipe_error ret;727728fenced_buf = CALLOC_STRUCT(fenced_buffer);729if(!fenced_buf)730goto no_buffer;731732pipe_reference_init(&fenced_buf->base.reference, 1);733fenced_buf->base.alignment_log2 = util_logbase2(desc->alignment);734fenced_buf->base.usage = desc->usage;735fenced_buf->base.size = size;736fenced_buf->size = size;737738fenced_buf->base.vtbl = &fenced_buffer_vtbl;739fenced_buf->mgr = fenced_mgr;740741mtx_lock(&fenced_mgr->mutex);742743/*744* Try to create GPU storage without stalling,745*/746ret = fenced_buffer_create_gpu_storage_locked(fenced_mgr, fenced_buf,747desc, TRUE);748749/*750* Give up.751*/752if(ret != PIPE_OK) {753goto no_storage;754}755756assert(fenced_buf->buffer);757758list_addtail(&fenced_buf->head, &fenced_mgr->unfenced);759++fenced_mgr->num_unfenced;760mtx_unlock(&fenced_mgr->mutex);761762return &fenced_buf->base;763764no_storage:765mtx_unlock(&fenced_mgr->mutex);766FREE(fenced_buf);767no_buffer:768return NULL;769}770771772static void773fenced_bufmgr_flush(struct pb_manager *mgr)774{775struct fenced_manager *fenced_mgr = fenced_manager(mgr);776777mtx_lock(&fenced_mgr->mutex);778while(fenced_manager_check_signalled_locked(fenced_mgr, TRUE))779;780mtx_unlock(&fenced_mgr->mutex);781782assert(fenced_mgr->provider->flush);783if(fenced_mgr->provider->flush)784fenced_mgr->provider->flush(fenced_mgr->provider);785}786787788static void789fenced_bufmgr_destroy(struct pb_manager *mgr)790{791struct fenced_manager *fenced_mgr = fenced_manager(mgr);792793mtx_lock(&fenced_mgr->mutex);794795/* Wait on outstanding fences */796while (fenced_mgr->num_fenced) {797mtx_unlock(&fenced_mgr->mutex);798#if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS)799sched_yield();800#endif801mtx_lock(&fenced_mgr->mutex);802while(fenced_manager_check_signalled_locked(fenced_mgr, TRUE))803;804}805806#ifdef DEBUG807/*assert(!fenced_mgr->num_unfenced);*/808#endif809810mtx_unlock(&fenced_mgr->mutex);811mtx_destroy(&fenced_mgr->mutex);812813FREE(fenced_mgr);814}815816817struct pb_manager *818simple_fenced_bufmgr_create(struct pb_manager *provider,819struct pb_fence_ops *ops)820{821struct fenced_manager *fenced_mgr;822823if(!provider)824return NULL;825826fenced_mgr = CALLOC_STRUCT(fenced_manager);827if (!fenced_mgr)828return NULL;829830fenced_mgr->base.destroy = fenced_bufmgr_destroy;831fenced_mgr->base.create_buffer = fenced_bufmgr_create_buffer;832fenced_mgr->base.flush = fenced_bufmgr_flush;833834fenced_mgr->provider = provider;835fenced_mgr->ops = ops;836837list_inithead(&fenced_mgr->fenced);838fenced_mgr->num_fenced = 0;839840list_inithead(&fenced_mgr->unfenced);841fenced_mgr->num_unfenced = 0;842843(void) mtx_init(&fenced_mgr->mutex, mtx_plain);844845return &fenced_mgr->base;846}847848849