Home | History | Annotate | Download | only in drm
      1 /**********************************************************
      2  * Copyright 2009-2011 VMware, Inc.  All rights reserved.
      3  *
      4  * Permission is hereby granted, free of charge, to any person
      5  * obtaining a copy of this software and associated documentation
      6  * files (the "Software"), to deal in the Software without
      7  * restriction, including without limitation the rights to use, copy,
      8  * modify, merge, publish, distribute, sublicense, and/or sell copies
      9  * of the Software, and to permit persons to whom the Software is
     10  * furnished to do so, subject to the following conditions:
     11  *
     12  * The above copyright notice and this permission notice shall be
     13  * included in all copies or substantial portions of the Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
     19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     22  * SOFTWARE.
     23  *
     24  **********************************************************/
     25 /*
     26  * TODO:
     27  *
     28  * Fencing is currently a bit inefficient, since we need to call the
     29  * kernel do determine a fence object signaled status if the fence is not
     30  * signaled. This can be greatly improved upon by using the fact that the
     31  * execbuf ioctl returns the last signaled fence seqno, as does the
     32  * fence signaled ioctl. We should set up a ring of fence objects and
     33  * walk through them checking for signaled status each time we receive a
     34  * new passed fence seqno.
     35  */
     36 
     37 #include "util/u_memory.h"
     38 #include "util/u_atomic.h"
     39 
     40 #include "pipebuffer/pb_buffer_fenced.h"
     41 
     42 #include "vmw_screen.h"
     43 #include "vmw_fence.h"
     44 
     45 struct vmw_fence_ops
     46 {
     47    struct pb_fence_ops base;
     48 
     49    struct vmw_winsys_screen *vws;
     50 };
     51 
     52 struct vmw_fence
     53 {
     54    int32_t refcount;
     55    uint32_t handle;
     56    uint32_t mask;
     57    int32_t signalled;
     58 };
     59 
     60 /**
     61  * vmw_fence - return the vmw_fence object identified by a
     62  * struct pipe_fence_handle *
     63  *
     64  * @fence: The opaque pipe fence handle.
     65  */
     66 static INLINE struct vmw_fence *
     67 vmw_fence(struct pipe_fence_handle *fence)
     68 {
     69    return (struct vmw_fence *) fence;
     70 }
     71 
     72 /**
     73  * vmw_fence_create - Create a user-space fence object.
     74  *
     75  * @handle: Handle identifying the kernel fence object.
     76  * @mask: Mask of flags that this fence object may signal.
     77  *
     78  * Returns NULL on failure.
     79  */
     80 struct pipe_fence_handle *
     81 vmw_fence_create(uint32_t handle, uint32_t mask)
     82 {
     83    struct vmw_fence *fence = CALLOC_STRUCT(vmw_fence);
     84 
     85    if (!fence)
     86       return NULL;
     87 
     88    p_atomic_set(&fence->refcount, 1);
     89    fence->handle = handle;
     90    fence->mask = mask;
     91    p_atomic_set(&fence->signalled, 0);
     92 
     93    return (struct pipe_fence_handle *) fence;
     94 }
     95 
     96 /**
     97  * vmw_fence_ops - Return the vmw_fence_ops structure backing a
     98  * struct pb_fence_ops pointer.
     99  *
    100  * @ops: Pointer to a struct pb_fence_ops.
    101  *
    102  */
    103 static INLINE struct vmw_fence_ops *
    104 vmw_fence_ops(struct pb_fence_ops *ops)
    105 {
    106    assert(ops);
    107    return (struct vmw_fence_ops *)ops;
    108 }
    109 
    110 
    111 
    112 /**
    113  * vmw_fence_reference - Reference / unreference a vmw fence object.
    114  *
    115  * @vws: Pointer to the winsys screen.
    116  * @ptr: Pointer to reference transfer destination.
    117  * @fence: Pointer to object to reference. May be NULL.
    118  */
    119 void
    120 vmw_fence_reference(struct vmw_winsys_screen *vws,
    121 		    struct pipe_fence_handle **ptr,
    122 		    struct pipe_fence_handle *fence)
    123 {
    124    if (*ptr) {
    125       struct vmw_fence *vfence = vmw_fence(*ptr);
    126 
    127       if (p_atomic_dec_zero(&vfence->refcount)) {
    128 	 vmw_ioctl_fence_unref(vws, vfence->handle);
    129 	 FREE(vfence);
    130       }
    131    }
    132 
    133    if (fence) {
    134       struct vmw_fence *vfence = vmw_fence(fence);
    135 
    136       p_atomic_inc(&vfence->refcount);
    137    }
    138 
    139    *ptr = fence;
    140 }
    141 
    142 
    143 /**
    144  * vmw_fence_signalled - Check whether a fence object is signalled.
    145  *
    146  * @vws: Pointer to the winsys screen.
    147  * @fence: Handle to the fence object.
    148  * @flag: Fence flags to check. If the fence object can't signal
    149  * a flag, it is assumed to be already signaled.
    150  *
    151  * Returns 0 if the fence object was signaled, nonzero otherwise.
    152  */
    153 int
    154 vmw_fence_signalled(struct vmw_winsys_screen *vws,
    155 		   struct pipe_fence_handle *fence,
    156 		   unsigned flag)
    157 {
    158    struct vmw_fence *vfence;
    159    int32_t vflags = SVGA_FENCE_FLAG_EXEC;
    160    int ret;
    161    uint32_t old;
    162 
    163    if (!fence)
    164       return 0;
    165 
    166    vfence = vmw_fence(fence);
    167    old = p_atomic_read(&vfence->signalled);
    168 
    169    vflags &= ~vfence->mask;
    170 
    171    if ((old & vflags) == vflags)
    172       return 0;
    173 
    174    ret = vmw_ioctl_fence_signalled(vws, vfence->handle, vflags);
    175 
    176    if (ret == 0) {
    177       int32_t prev = old;
    178 
    179       do {
    180 	 old = prev;
    181 	 prev = p_atomic_cmpxchg(&vfence->signalled, old, old | vflags);
    182       } while (prev != old);
    183    }
    184 
    185    return ret;
    186 }
    187 
    188 /**
    189  * vmw_fence_finish - Wait for a fence object to signal.
    190  *
    191  * @vws: Pointer to the winsys screen.
    192  * @fence: Handle to the fence object.
    193  * @flag: Fence flags to wait for. If the fence object can't signal
    194  * a flag, it is assumed to be already signaled.
    195  *
    196  * Returns 0 if the wait succeeded. Nonzero otherwise.
    197  */
    198 int
    199 vmw_fence_finish(struct vmw_winsys_screen *vws,
    200 		 struct pipe_fence_handle *fence,
    201 		 unsigned flag)
    202 {
    203    struct vmw_fence *vfence;
    204    int32_t vflags = SVGA_FENCE_FLAG_EXEC;
    205    int ret;
    206    uint32_t old;
    207 
    208    if (!fence)
    209       return 0;
    210 
    211    vfence = vmw_fence(fence);
    212    old = p_atomic_read(&vfence->signalled);
    213    vflags &= ~vfence->mask;
    214 
    215    if ((old & vflags) == vflags)
    216       return 0;
    217 
    218    ret = vmw_ioctl_fence_finish(vws, vfence->handle, vflags);
    219 
    220    if (ret == 0) {
    221       int32_t prev = old;
    222 
    223       do {
    224 	 old = prev;
    225 	 prev = p_atomic_cmpxchg(&vfence->signalled, old, old | vflags);
    226       } while (prev != old);
    227    }
    228 
    229    return ret;
    230 }
    231 
    232 
    233 /**
    234  * vmw_fence_ops_fence_reference - wrapper for the pb_fence_ops api.
    235  *
    236  * wrapper around vmw_fence_reference.
    237  */
    238 static void
    239 vmw_fence_ops_fence_reference(struct pb_fence_ops *ops,
    240                               struct pipe_fence_handle **ptr,
    241                               struct pipe_fence_handle *fence)
    242 {
    243    struct vmw_winsys_screen *vws = vmw_fence_ops(ops)->vws;
    244 
    245    vmw_fence_reference(vws, ptr, fence);
    246 }
    247 
    248 /**
    249  * vmw_fence_ops_fence_signalled - wrapper for the pb_fence_ops api.
    250  *
    251  * wrapper around vmw_fence_signalled.
    252  */
    253 static int
    254 vmw_fence_ops_fence_signalled(struct pb_fence_ops *ops,
    255                               struct pipe_fence_handle *fence,
    256                               unsigned flag)
    257 {
    258    struct vmw_winsys_screen *vws = vmw_fence_ops(ops)->vws;
    259 
    260    return vmw_fence_signalled(vws, fence, flag);
    261 }
    262 
    263 
    264 /**
    265  * vmw_fence_ops_fence_finish - wrapper for the pb_fence_ops api.
    266  *
    267  * wrapper around vmw_fence_finish.
    268  */
    269 static int
    270 vmw_fence_ops_fence_finish(struct pb_fence_ops *ops,
    271                            struct pipe_fence_handle *fence,
    272                            unsigned flag)
    273 {
    274    struct vmw_winsys_screen *vws = vmw_fence_ops(ops)->vws;
    275 
    276    return vmw_fence_finish(vws, fence, flag);
    277 }
    278 
    279 
    280 /**
    281  * vmw_fence_ops_destroy - Destroy a pb_fence_ops function table.
    282  *
    283  * @ops: The function table to destroy.
    284  *
    285  * Part of the pb_fence_ops api.
    286  */
    287 static void
    288 vmw_fence_ops_destroy(struct pb_fence_ops *ops)
    289 {
    290    FREE(ops);
    291 }
    292 
    293 
    294 /**
    295  * vmw_fence_ops_create - Create a pb_fence_ops function table.
    296  *
    297  * @vws: Pointer to a struct vmw_winsys_screen.
    298  *
    299  * Returns a pointer to a pb_fence_ops function table to interface
    300  * with pipe_buffer. This function is typically called on driver setup.
    301  *
    302  * Returns NULL on failure.
    303  */
    304 struct pb_fence_ops *
    305 vmw_fence_ops_create(struct vmw_winsys_screen *vws)
    306 {
    307    struct vmw_fence_ops *ops;
    308 
    309    ops = CALLOC_STRUCT(vmw_fence_ops);
    310    if(!ops)
    311       return NULL;
    312 
    313    ops->base.destroy = &vmw_fence_ops_destroy;
    314    ops->base.fence_reference = &vmw_fence_ops_fence_reference;
    315    ops->base.fence_signalled = &vmw_fence_ops_fence_signalled;
    316    ops->base.fence_finish = &vmw_fence_ops_fence_finish;
    317 
    318    ops->vws = vws;
    319 
    320    return &ops->base;
    321 }
    322 
    323 
    324