Home | History | Annotate | Download | only in drm
      1 /**********************************************************
      2  * Copyright 2009-2015 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 #include "util/u_memory.h"
     26 #include "util/u_atomic.h"
     27 #include "util/list.h"
     28 #include "os/os_thread.h"
     29 
     30 #include "pipebuffer/pb_buffer_fenced.h"
     31 
     32 #include "vmw_screen.h"
     33 #include "vmw_fence.h"
     34 
     35 struct vmw_fence_ops
     36 {
     37    /*
     38     * Immutable members.
     39     */
     40    struct pb_fence_ops base;
     41    struct vmw_winsys_screen *vws;
     42 
     43    pipe_mutex mutex;
     44 
     45    /*
     46     * Protected by mutex;
     47     */
     48    struct list_head not_signaled;
     49    uint32_t last_signaled;
     50    uint32_t last_emitted;
     51 };
     52 
     53 struct vmw_fence
     54 {
     55    struct list_head ops_list;
     56    int32_t refcount;
     57    uint32_t handle;
     58    uint32_t mask;
     59    int32_t signalled;
     60    uint32_t seqno;
     61 };
     62 
     63 /**
     64  * vmw_fence_seq_is_signaled - Check whether a fence seqno is
     65  * signaled.
     66  *
     67  * @ops: Pointer to a struct pb_fence_ops.
     68  *
     69  */
     70 static inline boolean
     71 vmw_fence_seq_is_signaled(uint32_t seq, uint32_t last, uint32_t cur)
     72 {
     73    return (cur - last <= cur - seq);
     74 }
     75 
     76 
     77 /**
     78  * vmw_fence_ops - Return the vmw_fence_ops structure backing a
     79  * struct pb_fence_ops pointer.
     80  *
     81  * @ops: Pointer to a struct pb_fence_ops.
     82  *
     83  */
     84 static inline struct vmw_fence_ops *
     85 vmw_fence_ops(struct pb_fence_ops *ops)
     86 {
     87    assert(ops);
     88    return (struct vmw_fence_ops *)ops;
     89 }
     90 
     91 
     92 /**
     93  * vmw_fences_release - Release all fences from the not_signaled
     94  * list.
     95  *
     96  * @ops: Pointer to a struct vmw_fence_ops.
     97  *
     98  */
     99 static void
    100 vmw_fences_release(struct vmw_fence_ops *ops)
    101 {
    102    struct vmw_fence *fence, *n;
    103 
    104    pipe_mutex_lock(ops->mutex);
    105    LIST_FOR_EACH_ENTRY_SAFE(fence, n, &ops->not_signaled, ops_list)
    106       LIST_DELINIT(&fence->ops_list);
    107    pipe_mutex_unlock(ops->mutex);
    108 }
    109 
    110 /**
    111  * vmw_fences_signal - Traverse the not_signaled list and try to
    112  * signal unsignaled fences.
    113  *
    114  * @ops: Pointer to a struct pb_fence_ops.
    115  * @signaled: Seqno that has signaled.
    116  * @emitted: Last seqno emitted by the kernel.
    117  * @has_emitted: Whether we provide the emitted value.
    118  *
    119  */
    120 void
    121 vmw_fences_signal(struct pb_fence_ops *fence_ops,
    122                   uint32_t signaled,
    123                   uint32_t emitted,
    124                   boolean has_emitted)
    125 {
    126    struct vmw_fence_ops *ops = NULL;
    127    struct vmw_fence *fence, *n;
    128 
    129    if (fence_ops == NULL)
    130       return;
    131 
    132    ops = vmw_fence_ops(fence_ops);
    133    pipe_mutex_lock(ops->mutex);
    134 
    135    if (!has_emitted) {
    136       emitted = ops->last_emitted;
    137       if (emitted - signaled > (1 << 30))
    138 	emitted = signaled;
    139    }
    140 
    141    if (signaled == ops->last_signaled && emitted == ops->last_emitted)
    142       goto out_unlock;
    143 
    144    LIST_FOR_EACH_ENTRY_SAFE(fence, n, &ops->not_signaled, ops_list) {
    145       if (!vmw_fence_seq_is_signaled(fence->seqno, signaled, emitted))
    146          break;
    147 
    148       p_atomic_set(&fence->signalled, 1);
    149       LIST_DELINIT(&fence->ops_list);
    150    }
    151    ops->last_signaled = signaled;
    152    ops->last_emitted = emitted;
    153 
    154 out_unlock:
    155    pipe_mutex_unlock(ops->mutex);
    156 }
    157 
    158 
    159 /**
    160  * vmw_fence - return the vmw_fence object identified by a
    161  * struct pipe_fence_handle *
    162  *
    163  * @fence: The opaque pipe fence handle.
    164  */
    165 static inline struct vmw_fence *
    166 vmw_fence(struct pipe_fence_handle *fence)
    167 {
    168    return (struct vmw_fence *) fence;
    169 }
    170 
    171 
    172 /**
    173  * vmw_fence_create - Create a user-space fence object.
    174  *
    175  * @fence_ops: The fence_ops manager to register with.
    176  * @handle: Handle identifying the kernel fence object.
    177  * @mask: Mask of flags that this fence object may signal.
    178  *
    179  * Returns NULL on failure.
    180  */
    181 struct pipe_fence_handle *
    182 vmw_fence_create(struct pb_fence_ops *fence_ops, uint32_t handle,
    183                  uint32_t seqno, uint32_t mask)
    184 {
    185    struct vmw_fence *fence = CALLOC_STRUCT(vmw_fence);
    186    struct vmw_fence_ops *ops = vmw_fence_ops(fence_ops);
    187 
    188    if (!fence)
    189       return NULL;
    190 
    191    p_atomic_set(&fence->refcount, 1);
    192    fence->handle = handle;
    193    fence->mask = mask;
    194    fence->seqno = seqno;
    195    p_atomic_set(&fence->signalled, 0);
    196    pipe_mutex_lock(ops->mutex);
    197 
    198    if (vmw_fence_seq_is_signaled(seqno, ops->last_signaled, seqno)) {
    199       p_atomic_set(&fence->signalled, 1);
    200       LIST_INITHEAD(&fence->ops_list);
    201    } else {
    202       p_atomic_set(&fence->signalled, 0);
    203       LIST_ADDTAIL(&fence->ops_list, &ops->not_signaled);
    204    }
    205 
    206    pipe_mutex_unlock(ops->mutex);
    207 
    208    return (struct pipe_fence_handle *) fence;
    209 }
    210 
    211 
    212 /**
    213  * vmw_fence_reference - Reference / unreference a vmw fence object.
    214  *
    215  * @vws: Pointer to the winsys screen.
    216  * @ptr: Pointer to reference transfer destination.
    217  * @fence: Pointer to object to reference. May be NULL.
    218  */
    219 void
    220 vmw_fence_reference(struct vmw_winsys_screen *vws,
    221 		    struct pipe_fence_handle **ptr,
    222 		    struct pipe_fence_handle *fence)
    223 {
    224    if (*ptr) {
    225       struct vmw_fence *vfence = vmw_fence(*ptr);
    226 
    227       if (p_atomic_dec_zero(&vfence->refcount)) {
    228          struct vmw_fence_ops *ops = vmw_fence_ops(vws->fence_ops);
    229 
    230 	 vmw_ioctl_fence_unref(vws, vfence->handle);
    231 
    232          pipe_mutex_lock(ops->mutex);
    233          LIST_DELINIT(&vfence->ops_list);
    234          pipe_mutex_unlock(ops->mutex);
    235 
    236 	 FREE(vfence);
    237       }
    238    }
    239 
    240    if (fence) {
    241       struct vmw_fence *vfence = vmw_fence(fence);
    242 
    243       p_atomic_inc(&vfence->refcount);
    244    }
    245 
    246    *ptr = fence;
    247 }
    248 
    249 
    250 /**
    251  * vmw_fence_signalled - Check whether a fence object is signalled.
    252  *
    253  * @vws: Pointer to the winsys screen.
    254  * @fence: Handle to the fence object.
    255  * @flag: Fence flags to check. If the fence object can't signal
    256  * a flag, it is assumed to be already signaled.
    257  *
    258  * Returns 0 if the fence object was signaled, nonzero otherwise.
    259  */
    260 int
    261 vmw_fence_signalled(struct vmw_winsys_screen *vws,
    262 		   struct pipe_fence_handle *fence,
    263 		   unsigned flag)
    264 {
    265    struct vmw_fence *vfence;
    266    int32_t vflags = SVGA_FENCE_FLAG_EXEC;
    267    int ret;
    268    uint32_t old;
    269 
    270    if (!fence)
    271       return 0;
    272 
    273    vfence = vmw_fence(fence);
    274    old = p_atomic_read(&vfence->signalled);
    275 
    276    vflags &= ~vfence->mask;
    277 
    278    if ((old & vflags) == vflags)
    279       return 0;
    280 
    281    /*
    282     * Currently we update signaled fences on each execbuf call.
    283     * That should really be sufficient, and we can avoid
    284     * a lot of kernel calls this way.
    285     */
    286 #if 1
    287    ret = vmw_ioctl_fence_signalled(vws, vfence->handle, vflags);
    288 
    289    if (ret == 0)
    290       p_atomic_set(&vfence->signalled, 1);
    291    return ret;
    292 #else
    293    (void) ret;
    294    return -1;
    295 #endif
    296 }
    297 
    298 /**
    299  * vmw_fence_finish - Wait for a fence object to signal.
    300  *
    301  * @vws: Pointer to the winsys screen.
    302  * @fence: Handle to the fence object.
    303  * @flag: Fence flags to wait for. If the fence object can't signal
    304  * a flag, it is assumed to be already signaled.
    305  *
    306  * Returns 0 if the wait succeeded. Nonzero otherwise.
    307  */
    308 int
    309 vmw_fence_finish(struct vmw_winsys_screen *vws,
    310 		 struct pipe_fence_handle *fence,
    311 		 unsigned flag)
    312 {
    313    struct vmw_fence *vfence;
    314    int32_t vflags = SVGA_FENCE_FLAG_EXEC;
    315    int ret;
    316    uint32_t old;
    317 
    318    if (!fence)
    319       return 0;
    320 
    321    vfence = vmw_fence(fence);
    322    old = p_atomic_read(&vfence->signalled);
    323    vflags &= ~vfence->mask;
    324 
    325    if ((old & vflags) == vflags)
    326       return 0;
    327 
    328    ret = vmw_ioctl_fence_finish(vws, vfence->handle, vflags);
    329 
    330    if (ret == 0) {
    331       int32_t prev = old;
    332 
    333       do {
    334 	 old = prev;
    335 	 prev = p_atomic_cmpxchg(&vfence->signalled, old, old | vflags);
    336       } while (prev != old);
    337    }
    338 
    339    return ret;
    340 }
    341 
    342 
    343 /**
    344  * vmw_fence_ops_fence_reference - wrapper for the pb_fence_ops api.
    345  *
    346  * wrapper around vmw_fence_reference.
    347  */
    348 static void
    349 vmw_fence_ops_fence_reference(struct pb_fence_ops *ops,
    350                               struct pipe_fence_handle **ptr,
    351                               struct pipe_fence_handle *fence)
    352 {
    353    struct vmw_winsys_screen *vws = vmw_fence_ops(ops)->vws;
    354 
    355    vmw_fence_reference(vws, ptr, fence);
    356 }
    357 
    358 /**
    359  * vmw_fence_ops_fence_signalled - wrapper for the pb_fence_ops api.
    360  *
    361  * wrapper around vmw_fence_signalled.
    362  */
    363 static int
    364 vmw_fence_ops_fence_signalled(struct pb_fence_ops *ops,
    365                               struct pipe_fence_handle *fence,
    366                               unsigned flag)
    367 {
    368    struct vmw_winsys_screen *vws = vmw_fence_ops(ops)->vws;
    369 
    370    return vmw_fence_signalled(vws, fence, flag);
    371 }
    372 
    373 
    374 /**
    375  * vmw_fence_ops_fence_finish - wrapper for the pb_fence_ops api.
    376  *
    377  * wrapper around vmw_fence_finish.
    378  */
    379 static int
    380 vmw_fence_ops_fence_finish(struct pb_fence_ops *ops,
    381                            struct pipe_fence_handle *fence,
    382                            unsigned flag)
    383 {
    384    struct vmw_winsys_screen *vws = vmw_fence_ops(ops)->vws;
    385 
    386    return vmw_fence_finish(vws, fence, flag);
    387 }
    388 
    389 
    390 /**
    391  * vmw_fence_ops_destroy - Destroy a pb_fence_ops function table.
    392  *
    393  * @ops: The function table to destroy.
    394  *
    395  * Part of the pb_fence_ops api.
    396  */
    397 static void
    398 vmw_fence_ops_destroy(struct pb_fence_ops *ops)
    399 {
    400    vmw_fences_release(vmw_fence_ops(ops));
    401    FREE(ops);
    402 }
    403 
    404 
    405 /**
    406  * vmw_fence_ops_create - Create a pb_fence_ops function table.
    407  *
    408  * @vws: Pointer to a struct vmw_winsys_screen.
    409  *
    410  * Returns a pointer to a pb_fence_ops function table to interface
    411  * with pipe_buffer. This function is typically called on driver setup.
    412  *
    413  * Returns NULL on failure.
    414  */
    415 struct pb_fence_ops *
    416 vmw_fence_ops_create(struct vmw_winsys_screen *vws)
    417 {
    418    struct vmw_fence_ops *ops;
    419 
    420    ops = CALLOC_STRUCT(vmw_fence_ops);
    421    if(!ops)
    422       return NULL;
    423 
    424    pipe_mutex_init(ops->mutex);
    425    LIST_INITHEAD(&ops->not_signaled);
    426    ops->base.destroy = &vmw_fence_ops_destroy;
    427    ops->base.fence_reference = &vmw_fence_ops_fence_reference;
    428    ops->base.fence_signalled = &vmw_fence_ops_fence_signalled;
    429    ops->base.fence_finish = &vmw_fence_ops_fence_finish;
    430 
    431    ops->vws = vws;
    432 
    433    return &ops->base;
    434 }
    435