Path: blob/21.2-virgl/src/gallium/winsys/svga/drm/vmw_fence.c
4573 views
/**********************************************************1* Copyright 2009-2015 VMware, Inc. All rights reserved.2*3* Permission is hereby granted, free of charge, to any person4* obtaining a copy of this software and associated documentation5* files (the "Software"), to deal in the Software without6* restriction, including without limitation the rights to use, copy,7* modify, merge, publish, distribute, sublicense, and/or sell copies8* of the Software, and to permit persons to whom the Software is9* furnished to do so, subject to the following conditions:10*11* The above copyright notice and this permission notice shall be12* included in all copies or substantial portions of the Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,15* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF16* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND17* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS18* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN19* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN20* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE21* SOFTWARE.22*23**********************************************************/24#include <libsync.h>2526#include "util/u_memory.h"27#include "util/u_atomic.h"28#include "util/list.h"29#include "os/os_thread.h"3031#include "pipebuffer/pb_buffer_fenced.h"3233#include "vmw_screen.h"34#include "vmw_fence.h"3536struct vmw_fence_ops37{38/*39* Immutable members.40*/41struct pb_fence_ops base;42struct vmw_winsys_screen *vws;4344mtx_t mutex;4546/*47* Protected by mutex;48*/49struct list_head not_signaled;50uint32_t last_signaled;51uint32_t last_emitted;52};5354struct vmw_fence55{56struct list_head ops_list;57int32_t refcount;58uint32_t handle;59uint32_t mask;60int32_t signalled;61uint32_t seqno;62int32_t fence_fd;63boolean imported; /* TRUE if imported from another process */64};6566/**67* vmw_fence_seq_is_signaled - Check whether a fence seqno is68* signaled.69*70* @ops: Pointer to a struct pb_fence_ops.71*72*/73static inline boolean74vmw_fence_seq_is_signaled(uint32_t seq, uint32_t last, uint32_t cur)75{76return (cur - last <= cur - seq);77}787980/**81* vmw_fence_ops - Return the vmw_fence_ops structure backing a82* struct pb_fence_ops pointer.83*84* @ops: Pointer to a struct pb_fence_ops.85*86*/87static inline struct vmw_fence_ops *88vmw_fence_ops(struct pb_fence_ops *ops)89{90assert(ops);91return (struct vmw_fence_ops *)ops;92}939495/**96* vmw_fences_release - Release all fences from the not_signaled97* list.98*99* @ops: Pointer to a struct vmw_fence_ops.100*101*/102static void103vmw_fences_release(struct vmw_fence_ops *ops)104{105struct vmw_fence *fence, *n;106107mtx_lock(&ops->mutex);108LIST_FOR_EACH_ENTRY_SAFE(fence, n, &ops->not_signaled, ops_list)109list_delinit(&fence->ops_list);110mtx_unlock(&ops->mutex);111}112113/**114* vmw_fences_signal - Traverse the not_signaled list and try to115* signal unsignaled fences.116*117* @ops: Pointer to a struct pb_fence_ops.118* @signaled: Seqno that has signaled.119* @emitted: Last seqno emitted by the kernel.120* @has_emitted: Whether we provide the emitted value.121*122*/123void124vmw_fences_signal(struct pb_fence_ops *fence_ops,125uint32_t signaled,126uint32_t emitted,127boolean has_emitted)128{129struct vmw_fence_ops *ops = NULL;130struct vmw_fence *fence, *n;131132if (fence_ops == NULL)133return;134135ops = vmw_fence_ops(fence_ops);136mtx_lock(&ops->mutex);137138if (!has_emitted) {139emitted = ops->last_emitted;140if (emitted - signaled > (1 << 30))141emitted = signaled;142}143144if (signaled == ops->last_signaled && emitted == ops->last_emitted)145goto out_unlock;146147LIST_FOR_EACH_ENTRY_SAFE(fence, n, &ops->not_signaled, ops_list) {148if (!vmw_fence_seq_is_signaled(fence->seqno, signaled, emitted))149break;150151p_atomic_set(&fence->signalled, 1);152list_delinit(&fence->ops_list);153}154ops->last_signaled = signaled;155ops->last_emitted = emitted;156157out_unlock:158mtx_unlock(&ops->mutex);159}160161162/**163* vmw_fence - return the vmw_fence object identified by a164* struct pipe_fence_handle *165*166* @fence: The opaque pipe fence handle.167*/168static inline struct vmw_fence *169vmw_fence(struct pipe_fence_handle *fence)170{171return (struct vmw_fence *) fence;172}173174175/**176* vmw_fence_create - Create a user-space fence object.177*178* @fence_ops: The fence_ops manager to register with.179* @handle: Handle identifying the kernel fence object.180* @mask: Mask of flags that this fence object may signal.181* @fd: File descriptor to associate with the fence182*183* Returns NULL on failure.184*/185struct pipe_fence_handle *186vmw_fence_create(struct pb_fence_ops *fence_ops, uint32_t handle,187uint32_t seqno, uint32_t mask, int32_t fd)188{189struct vmw_fence *fence = CALLOC_STRUCT(vmw_fence);190struct vmw_fence_ops *ops = NULL;191192if (!fence)193return NULL;194195p_atomic_set(&fence->refcount, 1);196fence->handle = handle;197fence->mask = mask;198fence->seqno = seqno;199fence->fence_fd = fd;200p_atomic_set(&fence->signalled, 0);201202/*203* If the fence was not created by our device, then we won't204* manage it with our ops205*/206if (!fence_ops) {207fence->imported = true;208return (struct pipe_fence_handle *) fence;209}210211ops = vmw_fence_ops(fence_ops);212213mtx_lock(&ops->mutex);214215if (vmw_fence_seq_is_signaled(seqno, ops->last_signaled, seqno)) {216p_atomic_set(&fence->signalled, 1);217list_inithead(&fence->ops_list);218} else {219p_atomic_set(&fence->signalled, 0);220list_addtail(&fence->ops_list, &ops->not_signaled);221}222223mtx_unlock(&ops->mutex);224225return (struct pipe_fence_handle *) fence;226}227228229/**230* vmw_fence_destroy - Frees a vmw fence object.231*232* Also closes the file handle associated with the object, if any233*/234static235void vmw_fence_destroy(struct vmw_fence *vfence)236{237if (vfence->fence_fd != -1)238close(vfence->fence_fd);239240FREE(vfence);241}242243244/**245* vmw_fence_reference - Reference / unreference a vmw fence object.246*247* @vws: Pointer to the winsys screen.248* @ptr: Pointer to reference transfer destination.249* @fence: Pointer to object to reference. May be NULL.250*/251void252vmw_fence_reference(struct vmw_winsys_screen *vws,253struct pipe_fence_handle **ptr,254struct pipe_fence_handle *fence)255{256if (*ptr) {257struct vmw_fence *vfence = vmw_fence(*ptr);258259if (p_atomic_dec_zero(&vfence->refcount)) {260struct vmw_fence_ops *ops = vmw_fence_ops(vws->fence_ops);261262if (!vfence->imported) {263vmw_ioctl_fence_unref(vws, vfence->handle);264265mtx_lock(&ops->mutex);266list_delinit(&vfence->ops_list);267mtx_unlock(&ops->mutex);268}269270vmw_fence_destroy(vfence);271}272}273274if (fence) {275struct vmw_fence *vfence = vmw_fence(fence);276277p_atomic_inc(&vfence->refcount);278}279280*ptr = fence;281}282283284/**285* vmw_fence_signalled - Check whether a fence object is signalled.286*287* @vws: Pointer to the winsys screen.288* @fence: Handle to the fence object.289* @flag: Fence flags to check. If the fence object can't signal290* a flag, it is assumed to be already signaled.291*292* Returns 0 if the fence object was signaled, nonzero otherwise.293*/294int295vmw_fence_signalled(struct vmw_winsys_screen *vws,296struct pipe_fence_handle *fence,297unsigned flag)298{299struct vmw_fence *vfence;300int32_t vflags = SVGA_FENCE_FLAG_EXEC;301int ret;302uint32_t old;303304if (!fence)305return 0;306307vfence = vmw_fence(fence);308old = p_atomic_read(&vfence->signalled);309310vflags &= ~vfence->mask;311312if ((old & vflags) == vflags)313return 0;314315/*316* Currently we update signaled fences on each execbuf call.317* That should really be sufficient, and we can avoid318* a lot of kernel calls this way.319*/320#if 1321ret = vmw_ioctl_fence_signalled(vws, vfence->handle, vflags);322323if (ret == 0)324p_atomic_set(&vfence->signalled, 1);325return ret;326#else327(void) ret;328return -1;329#endif330}331332/**333* vmw_fence_finish - Wait for a fence object to signal.334*335* @vws: Pointer to the winsys screen.336* @fence: Handle to the fence object.337* @timeout: How long to wait before timing out.338* @flag: Fence flags to wait for. If the fence object can't signal339* a flag, it is assumed to be already signaled.340*341* Returns 0 if the wait succeeded. Nonzero otherwise.342*/343int344vmw_fence_finish(struct vmw_winsys_screen *vws,345struct pipe_fence_handle *fence,346uint64_t timeout,347unsigned flag)348{349struct vmw_fence *vfence;350int32_t vflags = SVGA_FENCE_FLAG_EXEC;351int ret;352uint32_t old;353354if (!fence)355return 0;356357vfence = vmw_fence(fence);358359if (vfence->imported) {360ret = sync_wait(vfence->fence_fd, timeout / 1000000);361362if (!ret)363p_atomic_set(&vfence->signalled, 1);364365return !!ret;366}367368old = p_atomic_read(&vfence->signalled);369vflags &= ~vfence->mask;370371if ((old & vflags) == vflags)372return 0;373374ret = vmw_ioctl_fence_finish(vws, vfence->handle, vflags);375376if (ret == 0) {377int32_t prev = old;378379do {380old = prev;381prev = p_atomic_cmpxchg(&vfence->signalled, old, old | vflags);382} while (prev != old);383}384385return ret;386}387388/**389* vmw_fence_get_fd390*391* Returns the file descriptor associated with the fence392*/393int394vmw_fence_get_fd(struct pipe_fence_handle *fence)395{396struct vmw_fence *vfence;397398if (!fence)399return -1;400401vfence = vmw_fence(fence);402return vfence->fence_fd;403}404405406/**407* vmw_fence_ops_fence_reference - wrapper for the pb_fence_ops api.408*409* wrapper around vmw_fence_reference.410*/411static void412vmw_fence_ops_fence_reference(struct pb_fence_ops *ops,413struct pipe_fence_handle **ptr,414struct pipe_fence_handle *fence)415{416struct vmw_winsys_screen *vws = vmw_fence_ops(ops)->vws;417418vmw_fence_reference(vws, ptr, fence);419}420421/**422* vmw_fence_ops_fence_signalled - wrapper for the pb_fence_ops api.423*424* wrapper around vmw_fence_signalled.425*/426static int427vmw_fence_ops_fence_signalled(struct pb_fence_ops *ops,428struct pipe_fence_handle *fence,429unsigned flag)430{431struct vmw_winsys_screen *vws = vmw_fence_ops(ops)->vws;432433return vmw_fence_signalled(vws, fence, flag);434}435436437/**438* vmw_fence_ops_fence_finish - wrapper for the pb_fence_ops api.439*440* wrapper around vmw_fence_finish.441*/442static int443vmw_fence_ops_fence_finish(struct pb_fence_ops *ops,444struct pipe_fence_handle *fence,445unsigned flag)446{447struct vmw_winsys_screen *vws = vmw_fence_ops(ops)->vws;448449return vmw_fence_finish(vws, fence, PIPE_TIMEOUT_INFINITE, flag);450}451452453/**454* vmw_fence_ops_destroy - Destroy a pb_fence_ops function table.455*456* @ops: The function table to destroy.457*458* Part of the pb_fence_ops api.459*/460static void461vmw_fence_ops_destroy(struct pb_fence_ops *ops)462{463vmw_fences_release(vmw_fence_ops(ops));464FREE(ops);465}466467468/**469* vmw_fence_ops_create - Create a pb_fence_ops function table.470*471* @vws: Pointer to a struct vmw_winsys_screen.472*473* Returns a pointer to a pb_fence_ops function table to interface474* with pipe_buffer. This function is typically called on driver setup.475*476* Returns NULL on failure.477*/478struct pb_fence_ops *479vmw_fence_ops_create(struct vmw_winsys_screen *vws)480{481struct vmw_fence_ops *ops;482483ops = CALLOC_STRUCT(vmw_fence_ops);484if(!ops)485return NULL;486487(void) mtx_init(&ops->mutex, mtx_plain);488list_inithead(&ops->not_signaled);489ops->base.destroy = &vmw_fence_ops_destroy;490ops->base.fence_reference = &vmw_fence_ops_fence_reference;491ops->base.fence_signalled = &vmw_fence_ops_fence_signalled;492ops->base.fence_finish = &vmw_fence_ops_fence_finish;493494ops->vws = vws;495496return &ops->base;497}498499500