1 /**********************************************************
2 * Copyright 2009-2015 VMware, Inc. All rights reserved.
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:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
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
24 **********************************************************/
27 #include "util/u_memory.h"
28 #include "util/u_atomic.h"
29 #include "util/list.h"
30 #include "os/os_thread.h"
32 #include "pipebuffer/pb_buffer_fenced.h"
34 #include "vmw_screen.h"
35 #include "vmw_fence.h"
42 struct pb_fence_ops base
;
43 struct vmw_winsys_screen
*vws
;
50 struct list_head not_signaled
;
51 uint32_t last_signaled
;
52 uint32_t last_emitted
;
57 struct list_head ops_list
;
64 boolean imported
; /* TRUE if imported from another process */
68 * vmw_fence_seq_is_signaled - Check whether a fence seqno is
71 * @ops: Pointer to a struct pb_fence_ops.
75 vmw_fence_seq_is_signaled(uint32_t seq
, uint32_t last
, uint32_t cur
)
77 return (cur
- last
<= cur
- seq
);
82 * vmw_fence_ops - Return the vmw_fence_ops structure backing a
83 * struct pb_fence_ops pointer.
85 * @ops: Pointer to a struct pb_fence_ops.
88 static inline struct vmw_fence_ops
*
89 vmw_fence_ops(struct pb_fence_ops
*ops
)
92 return (struct vmw_fence_ops
*)ops
;
97 * vmw_fences_release - Release all fences from the not_signaled
100 * @ops: Pointer to a struct vmw_fence_ops.
104 vmw_fences_release(struct vmw_fence_ops
*ops
)
106 struct vmw_fence
*fence
, *n
;
108 mtx_lock(&ops
->mutex
);
109 LIST_FOR_EACH_ENTRY_SAFE(fence
, n
, &ops
->not_signaled
, ops_list
)
110 LIST_DELINIT(&fence
->ops_list
);
111 mtx_unlock(&ops
->mutex
);
115 * vmw_fences_signal - Traverse the not_signaled list and try to
116 * signal unsignaled fences.
118 * @ops: Pointer to a struct pb_fence_ops.
119 * @signaled: Seqno that has signaled.
120 * @emitted: Last seqno emitted by the kernel.
121 * @has_emitted: Whether we provide the emitted value.
125 vmw_fences_signal(struct pb_fence_ops
*fence_ops
,
130 struct vmw_fence_ops
*ops
= NULL
;
131 struct vmw_fence
*fence
, *n
;
133 if (fence_ops
== NULL
)
136 ops
= vmw_fence_ops(fence_ops
);
137 mtx_lock(&ops
->mutex
);
140 emitted
= ops
->last_emitted
;
141 if (emitted
- signaled
> (1 << 30))
145 if (signaled
== ops
->last_signaled
&& emitted
== ops
->last_emitted
)
148 LIST_FOR_EACH_ENTRY_SAFE(fence
, n
, &ops
->not_signaled
, ops_list
) {
149 if (!vmw_fence_seq_is_signaled(fence
->seqno
, signaled
, emitted
))
152 p_atomic_set(&fence
->signalled
, 1);
153 LIST_DELINIT(&fence
->ops_list
);
155 ops
->last_signaled
= signaled
;
156 ops
->last_emitted
= emitted
;
159 mtx_unlock(&ops
->mutex
);
164 * vmw_fence - return the vmw_fence object identified by a
165 * struct pipe_fence_handle *
167 * @fence: The opaque pipe fence handle.
169 static inline struct vmw_fence
*
170 vmw_fence(struct pipe_fence_handle
*fence
)
172 return (struct vmw_fence
*) fence
;
177 * vmw_fence_create - Create a user-space fence object.
179 * @fence_ops: The fence_ops manager to register with.
180 * @handle: Handle identifying the kernel fence object.
181 * @mask: Mask of flags that this fence object may signal.
182 * @fd: File descriptor to associate with the fence
184 * Returns NULL on failure.
186 struct pipe_fence_handle
*
187 vmw_fence_create(struct pb_fence_ops
*fence_ops
, uint32_t handle
,
188 uint32_t seqno
, uint32_t mask
, int32_t fd
)
190 struct vmw_fence
*fence
= CALLOC_STRUCT(vmw_fence
);
191 struct vmw_fence_ops
*ops
= NULL
;
196 p_atomic_set(&fence
->refcount
, 1);
197 fence
->handle
= handle
;
199 fence
->seqno
= seqno
;
200 fence
->fence_fd
= fd
;
201 p_atomic_set(&fence
->signalled
, 0);
204 * If the fence was not created by our device, then we won't
205 * manage it with our ops
208 fence
->imported
= true;
209 return (struct pipe_fence_handle
*) fence
;
212 ops
= vmw_fence_ops(fence_ops
);
214 mtx_lock(&ops
->mutex
);
216 if (vmw_fence_seq_is_signaled(seqno
, ops
->last_signaled
, seqno
)) {
217 p_atomic_set(&fence
->signalled
, 1);
218 list_inithead(&fence
->ops_list
);
220 p_atomic_set(&fence
->signalled
, 0);
221 list_addtail(&fence
->ops_list
, &ops
->not_signaled
);
224 mtx_unlock(&ops
->mutex
);
226 return (struct pipe_fence_handle
*) fence
;
231 * vmw_fence_destroy - Frees a vmw fence object.
233 * Also closes the file handle associated with the object, if any
236 void vmw_fence_destroy(struct vmw_fence
*vfence
)
238 if (vfence
->fence_fd
!= -1)
239 close(vfence
->fence_fd
);
246 * vmw_fence_reference - Reference / unreference a vmw fence object.
248 * @vws: Pointer to the winsys screen.
249 * @ptr: Pointer to reference transfer destination.
250 * @fence: Pointer to object to reference. May be NULL.
253 vmw_fence_reference(struct vmw_winsys_screen
*vws
,
254 struct pipe_fence_handle
**ptr
,
255 struct pipe_fence_handle
*fence
)
258 struct vmw_fence
*vfence
= vmw_fence(*ptr
);
260 if (p_atomic_dec_zero(&vfence
->refcount
)) {
261 struct vmw_fence_ops
*ops
= vmw_fence_ops(vws
->fence_ops
);
263 if (!vfence
->imported
) {
264 vmw_ioctl_fence_unref(vws
, vfence
->handle
);
266 mtx_lock(&ops
->mutex
);
267 LIST_DELINIT(&vfence
->ops_list
);
268 mtx_unlock(&ops
->mutex
);
271 vmw_fence_destroy(vfence
);
276 struct vmw_fence
*vfence
= vmw_fence(fence
);
278 p_atomic_inc(&vfence
->refcount
);
286 * vmw_fence_signalled - Check whether a fence object is signalled.
288 * @vws: Pointer to the winsys screen.
289 * @fence: Handle to the fence object.
290 * @flag: Fence flags to check. If the fence object can't signal
291 * a flag, it is assumed to be already signaled.
293 * Returns 0 if the fence object was signaled, nonzero otherwise.
296 vmw_fence_signalled(struct vmw_winsys_screen
*vws
,
297 struct pipe_fence_handle
*fence
,
300 struct vmw_fence
*vfence
;
301 int32_t vflags
= SVGA_FENCE_FLAG_EXEC
;
308 vfence
= vmw_fence(fence
);
309 old
= p_atomic_read(&vfence
->signalled
);
311 vflags
&= ~vfence
->mask
;
313 if ((old
& vflags
) == vflags
)
317 * Currently we update signaled fences on each execbuf call.
318 * That should really be sufficient, and we can avoid
319 * a lot of kernel calls this way.
322 ret
= vmw_ioctl_fence_signalled(vws
, vfence
->handle
, vflags
);
325 p_atomic_set(&vfence
->signalled
, 1);
334 * vmw_fence_finish - Wait for a fence object to signal.
336 * @vws: Pointer to the winsys screen.
337 * @fence: Handle to the fence object.
338 * @timeout: How long to wait before timing out.
339 * @flag: Fence flags to wait for. If the fence object can't signal
340 * a flag, it is assumed to be already signaled.
342 * Returns 0 if the wait succeeded. Nonzero otherwise.
345 vmw_fence_finish(struct vmw_winsys_screen
*vws
,
346 struct pipe_fence_handle
*fence
,
350 struct vmw_fence
*vfence
;
351 int32_t vflags
= SVGA_FENCE_FLAG_EXEC
;
358 vfence
= vmw_fence(fence
);
360 if (vfence
->imported
) {
361 ret
= sync_wait(vfence
->fence_fd
, timeout
/ 1000000);
364 p_atomic_set(&vfence
->signalled
, 1);
369 old
= p_atomic_read(&vfence
->signalled
);
370 vflags
&= ~vfence
->mask
;
372 if ((old
& vflags
) == vflags
)
375 ret
= vmw_ioctl_fence_finish(vws
, vfence
->handle
, vflags
);
382 prev
= p_atomic_cmpxchg(&vfence
->signalled
, old
, old
| vflags
);
383 } while (prev
!= old
);
392 * Returns the file descriptor associated with the fence
395 vmw_fence_get_fd(struct pipe_fence_handle
*fence
)
397 struct vmw_fence
*vfence
;
402 vfence
= vmw_fence(fence
);
403 return vfence
->fence_fd
;
408 * vmw_fence_ops_fence_reference - wrapper for the pb_fence_ops api.
410 * wrapper around vmw_fence_reference.
413 vmw_fence_ops_fence_reference(struct pb_fence_ops
*ops
,
414 struct pipe_fence_handle
**ptr
,
415 struct pipe_fence_handle
*fence
)
417 struct vmw_winsys_screen
*vws
= vmw_fence_ops(ops
)->vws
;
419 vmw_fence_reference(vws
, ptr
, fence
);
423 * vmw_fence_ops_fence_signalled - wrapper for the pb_fence_ops api.
425 * wrapper around vmw_fence_signalled.
428 vmw_fence_ops_fence_signalled(struct pb_fence_ops
*ops
,
429 struct pipe_fence_handle
*fence
,
432 struct vmw_winsys_screen
*vws
= vmw_fence_ops(ops
)->vws
;
434 return vmw_fence_signalled(vws
, fence
, flag
);
439 * vmw_fence_ops_fence_finish - wrapper for the pb_fence_ops api.
441 * wrapper around vmw_fence_finish.
444 vmw_fence_ops_fence_finish(struct pb_fence_ops
*ops
,
445 struct pipe_fence_handle
*fence
,
448 struct vmw_winsys_screen
*vws
= vmw_fence_ops(ops
)->vws
;
450 return vmw_fence_finish(vws
, fence
, PIPE_TIMEOUT_INFINITE
, flag
);
455 * vmw_fence_ops_destroy - Destroy a pb_fence_ops function table.
457 * @ops: The function table to destroy.
459 * Part of the pb_fence_ops api.
462 vmw_fence_ops_destroy(struct pb_fence_ops
*ops
)
464 vmw_fences_release(vmw_fence_ops(ops
));
470 * vmw_fence_ops_create - Create a pb_fence_ops function table.
472 * @vws: Pointer to a struct vmw_winsys_screen.
474 * Returns a pointer to a pb_fence_ops function table to interface
475 * with pipe_buffer. This function is typically called on driver setup.
477 * Returns NULL on failure.
479 struct pb_fence_ops
*
480 vmw_fence_ops_create(struct vmw_winsys_screen
*vws
)
482 struct vmw_fence_ops
*ops
;
484 ops
= CALLOC_STRUCT(vmw_fence_ops
);
488 (void) mtx_init(&ops
->mutex
, mtx_plain
);
489 list_inithead(&ops
->not_signaled
);
490 ops
->base
.destroy
= &vmw_fence_ops_destroy
;
491 ops
->base
.fence_reference
= &vmw_fence_ops_fence_reference
;
492 ops
->base
.fence_signalled
= &vmw_fence_ops_fence_signalled
;
493 ops
->base
.fence_finish
= &vmw_fence_ops_fence_finish
;