1/*
2 * Copyright © 2019 Google LLC
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24#include "tu_private.h"
25
26#include <fcntl.h>
27#include <libsync.h>
28#include <unistd.h>
29
30#include "util/os_time.h"
31
32/**
33 * Internally, a fence can be in one of these states.
34 */
35enum tu_fence_state
36{
37   TU_FENCE_STATE_RESET,
38   TU_FENCE_STATE_PENDING,
39   TU_FENCE_STATE_SIGNALED,
40};
41
42static enum tu_fence_state
43tu_fence_get_state(const struct tu_fence *fence)
44{
45   if (fence->signaled)
46      assert(fence->fd < 0);
47
48   if (fence->signaled)
49      return TU_FENCE_STATE_SIGNALED;
50   else if (fence->fd >= 0)
51      return TU_FENCE_STATE_PENDING;
52   else
53      return TU_FENCE_STATE_RESET;
54}
55
56static void
57tu_fence_set_state(struct tu_fence *fence, enum tu_fence_state state, int fd)
58{
59   if (fence->fd >= 0)
60      close(fence->fd);
61
62   switch (state) {
63   case TU_FENCE_STATE_RESET:
64      assert(fd < 0);
65      fence->signaled = false;
66      fence->fd = -1;
67      break;
68   case TU_FENCE_STATE_PENDING:
69      assert(fd >= 0);
70      fence->signaled = false;
71      fence->fd = fd;
72      break;
73   case TU_FENCE_STATE_SIGNALED:
74      assert(fd < 0);
75      fence->signaled = true;
76      fence->fd = -1;
77      break;
78   default:
79      unreachable("unknown fence state");
80      break;
81   }
82}
83
84void
85tu_fence_init(struct tu_fence *fence, bool signaled)
86{
87   fence->signaled = signaled;
88   fence->fd = -1;
89}
90
91void
92tu_fence_finish(struct tu_fence *fence)
93{
94   if (fence->fd >= 0)
95      close(fence->fd);
96}
97
98/**
99 * Update the associated fd of a fence.  Ownership of \a fd is transferred to
100 * \a fence.
101 *
102 * This function does not block.  \a fence can also be in any state when this
103 * function is called.  To be able to do that, the caller must make sure that,
104 * when both the currently associated fd and the new fd are valid, they are on
105 * the same timeline with the new fd being later on the timeline.
106 */
107void
108tu_fence_update_fd(struct tu_fence *fence, int fd)
109{
110   const enum tu_fence_state state =
111      fd >= 0 ? TU_FENCE_STATE_PENDING : TU_FENCE_STATE_SIGNALED;
112   tu_fence_set_state(fence, state, fd);
113}
114
115/**
116 * Make a fence a copy of another fence.  \a fence must be in the reset state.
117 */
118void
119tu_fence_copy(struct tu_fence *fence, const struct tu_fence *src)
120{
121   assert(tu_fence_get_state(fence) == TU_FENCE_STATE_RESET);
122
123   /* dup src->fd */
124   int fd = -1;
125   if (src->fd >= 0) {
126      fd = fcntl(src->fd, F_DUPFD_CLOEXEC, 0);
127      if (fd < 0) {
128         tu_loge("failed to dup fd %d for fence", src->fd);
129         sync_wait(src->fd, -1);
130      }
131   }
132
133   tu_fence_update_fd(fence, fd);
134}
135
136/**
137 * Signal a fence.  \a fence must be in the reset state.
138 */
139void
140tu_fence_signal(struct tu_fence *fence)
141{
142   assert(tu_fence_get_state(fence) == TU_FENCE_STATE_RESET);
143   tu_fence_set_state(fence, TU_FENCE_STATE_SIGNALED, -1);
144}
145
146/**
147 * Wait until a fence is idle (i.e., not pending).
148 */
149void
150tu_fence_wait_idle(struct tu_fence *fence)
151{
152   if (fence->fd >= 0) {
153      if (sync_wait(fence->fd, -1))
154         tu_loge("sync_wait on fence fd %d failed", fence->fd);
155
156      tu_fence_set_state(fence, TU_FENCE_STATE_SIGNALED, -1);
157   }
158}
159
160VkResult
161tu_CreateFence(VkDevice _device,
162               const VkFenceCreateInfo *pCreateInfo,
163               const VkAllocationCallbacks *pAllocator,
164               VkFence *pFence)
165{
166   TU_FROM_HANDLE(tu_device, device, _device);
167
168   struct tu_fence *fence =
169      vk_alloc2(&device->alloc, pAllocator, sizeof(*fence), 8,
170                VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
171
172   if (!fence)
173      return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
174
175   tu_fence_init(fence, pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT);
176
177   *pFence = tu_fence_to_handle(fence);
178
179   return VK_SUCCESS;
180}
181
182void
183tu_DestroyFence(VkDevice _device,
184                VkFence _fence,
185                const VkAllocationCallbacks *pAllocator)
186{
187   TU_FROM_HANDLE(tu_device, device, _device);
188   TU_FROM_HANDLE(tu_fence, fence, _fence);
189
190   if (!fence)
191      return;
192
193   tu_fence_finish(fence);
194
195   vk_free2(&device->alloc, pAllocator, fence);
196}
197
198/**
199 * Initialize a pollfd array from fences.
200 */
201static nfds_t
202tu_fence_init_poll_fds(uint32_t fence_count,
203                       const VkFence *fences,
204                       bool wait_all,
205                       struct pollfd *fds)
206{
207   nfds_t nfds = 0;
208   for (uint32_t i = 0; i < fence_count; i++) {
209      TU_FROM_HANDLE(tu_fence, fence, fences[i]);
210
211      if (fence->signaled) {
212         if (wait_all) {
213            /* skip signaled fences */
214            continue;
215         } else {
216            /* no need to poll any fd */
217            nfds = 0;
218            break;
219         }
220      }
221
222      /* negative fds are never ready, which is the desired behavior */
223      fds[nfds].fd = fence->fd;
224      fds[nfds].events = POLLIN;
225      fds[nfds].revents = 0;
226      nfds++;
227   }
228
229   return nfds;
230}
231
232/**
233 * Translate timeout from nanoseconds to milliseconds for poll().
234 */
235static int
236tu_fence_get_poll_timeout(uint64_t timeout_ns)
237{
238   const uint64_t ns_per_ms = 1000 * 1000;
239   uint64_t timeout_ms = timeout_ns / ns_per_ms;
240
241   /* round up if needed */
242   if (timeout_ns - timeout_ms * ns_per_ms >= ns_per_ms / 2)
243      timeout_ms++;
244
245   return timeout_ms < INT_MAX ? timeout_ms : INT_MAX;
246}
247
248/**
249 * Poll a pollfd array.
250 */
251static VkResult
252tu_fence_poll_fds(struct pollfd *fds, nfds_t nfds, uint64_t *timeout_ns)
253{
254   while (true) {
255      /* poll */
256      uint64_t duration = os_time_get_nano();
257      int ret = poll(fds, nfds, tu_fence_get_poll_timeout(*timeout_ns));
258      duration = os_time_get_nano() - duration;
259
260      /* update timeout_ns */
261      if (*timeout_ns > duration)
262         *timeout_ns -= duration;
263      else
264         *timeout_ns = 0;
265
266      if (ret > 0) {
267         return VK_SUCCESS;
268      } else if (ret == 0) {
269         if (!*timeout_ns)
270            return VK_TIMEOUT;
271      } else if (errno != EINTR && errno != EAGAIN) {
272         return VK_ERROR_OUT_OF_HOST_MEMORY;
273      }
274   }
275}
276
277/**
278 * Update a pollfd array and the fence states.  This should be called after a
279 * successful call to tu_fence_poll_fds.
280 */
281static nfds_t
282tu_fence_update_fences_and_poll_fds(uint32_t fence_count,
283                                    const VkFence *fences,
284                                    bool wait_all,
285                                    struct pollfd *fds)
286{
287   uint32_t nfds = 0;
288   uint32_t fds_idx = 0;
289   for (uint32_t i = 0; i < fence_count; i++) {
290      TU_FROM_HANDLE(tu_fence, fence, fences[i]);
291
292      /* no signaled fence in fds */
293      if (fence->signaled)
294         continue;
295
296      /* fds[fds_idx] corresponds to fences[i] */
297      assert(fence->fd == fds[fds_idx].fd);
298
299      assert(nfds <= fds_idx && fds_idx <= i);
300
301      /* fd is ready (errors are treated as ready) */
302      if (fds[fds_idx].revents) {
303         tu_fence_set_state(fence, TU_FENCE_STATE_SIGNALED, -1);
304      } else if (wait_all) {
305         /* add to fds again for another poll */
306         fds[nfds].fd = fence->fd;
307         fds[nfds].events = POLLIN;
308         fds[nfds].revents = 0;
309         nfds++;
310      }
311
312      fds_idx++;
313   }
314
315   return nfds;
316}
317
318VkResult
319tu_WaitForFences(VkDevice _device,
320                 uint32_t fenceCount,
321                 const VkFence *pFences,
322                 VkBool32 waitAll,
323                 uint64_t timeout)
324{
325   TU_FROM_HANDLE(tu_device, device, _device);
326
327   /* add a simpler path for when fenceCount == 1? */
328
329   struct pollfd stack_fds[8];
330   struct pollfd *fds = stack_fds;
331   if (fenceCount > ARRAY_SIZE(stack_fds)) {
332      fds = vk_alloc(&device->alloc, sizeof(*fds) * fenceCount, 8,
333                     VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
334      if (!fds)
335         return VK_ERROR_OUT_OF_HOST_MEMORY;
336   }
337
338   /* set up pollfd array and start polling */
339   nfds_t nfds = tu_fence_init_poll_fds(fenceCount, pFences, waitAll, fds);
340   VkResult result = VK_SUCCESS;
341   while (nfds) {
342      result = tu_fence_poll_fds(fds, nfds, &timeout);
343      if (result != VK_SUCCESS)
344         break;
345      nfds = tu_fence_update_fences_and_poll_fds(fenceCount, pFences, waitAll,
346                                                 fds);
347   }
348
349   if (fds != stack_fds)
350      vk_free(&device->alloc, fds);
351
352   return result;
353}
354
355VkResult
356tu_ResetFences(VkDevice _device, uint32_t fenceCount, const VkFence *pFences)
357{
358   for (unsigned i = 0; i < fenceCount; ++i) {
359      TU_FROM_HANDLE(tu_fence, fence, pFences[i]);
360      assert(tu_fence_get_state(fence) != TU_FENCE_STATE_PENDING);
361      tu_fence_set_state(fence, TU_FENCE_STATE_RESET, -1);
362   }
363
364   return VK_SUCCESS;
365}
366
367VkResult
368tu_GetFenceStatus(VkDevice _device, VkFence _fence)
369{
370   TU_FROM_HANDLE(tu_fence, fence, _fence);
371
372   if (fence->fd >= 0) {
373      int err = sync_wait(fence->fd, 0);
374      if (!err)
375         tu_fence_set_state(fence, TU_FENCE_STATE_SIGNALED, -1);
376      else if (err && errno != ETIME)
377         return VK_ERROR_OUT_OF_HOST_MEMORY;
378   }
379
380   return fence->signaled ? VK_SUCCESS : VK_NOT_READY;
381}
382