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
45struct vmw_fence_ops
46{
47   struct pb_fence_ops base;
48
49   struct vmw_winsys_screen *vws;
50};
51
52struct 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 */
66static INLINE struct vmw_fence *
67vmw_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 */
80struct pipe_fence_handle *
81vmw_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 */
103static INLINE struct vmw_fence_ops *
104vmw_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 */
119void
120vmw_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 */
153int
154vmw_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 */
198int
199vmw_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 */
238static void
239vmw_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 */
253static int
254vmw_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 */
269static int
270vmw_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 */
287static void
288vmw_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 */
304struct pb_fence_ops *
305vmw_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