1 /*
2 * Copyright 2022 Google LLC
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include <poll.h>
7 #include <string.h>
8
9 #include "virgl_context.h"
10 #include "virgl_util.h"
11
12 #include "util/os_file.h"
13 #include "util/u_atomic.h"
14 #include "util/u_thread.h"
15
16 #include "drm_fence.h"
17 #include "drm_util.h"
18
19 /**
20 * Tracking for a single fence on a timeline
21 */
22 struct drm_fence {
23 int fd;
24 uint32_t flags;
25 uint64_t fence_id;
26 struct list_head node;
27 };
28
29 static void
drm_fence_destroy(struct drm_fence * fence)30 drm_fence_destroy(struct drm_fence *fence)
31 {
32 close(fence->fd);
33 list_del(&fence->node);
34 free(fence);
35 }
36
37 static struct drm_fence *
drm_fence_create(int fd,uint32_t flags,uint64_t fence_id)38 drm_fence_create(int fd, uint32_t flags, uint64_t fence_id)
39 {
40 struct drm_fence *fence = calloc(1, sizeof(*fence));
41
42 if (!fence)
43 return NULL;
44
45 fence->fd = os_dupfd_cloexec(fd);
46
47 if (fence->fd < 0) {
48 free(fence);
49 return NULL;
50 }
51
52 fence->flags = flags;
53 fence->fence_id = fence_id;
54
55 return fence;
56 }
57
58 static int
thread_sync(void * arg)59 thread_sync(void *arg)
60 {
61 struct drm_timeline *timeline = arg;
62
63 u_thread_setname(timeline->name);
64
65 mtx_lock(&timeline->fence_mutex);
66 while (!timeline->stop_sync_thread) {
67 if (list_is_empty(&timeline->pending_fences)) {
68 if (cnd_wait(&timeline->fence_cond, &timeline->fence_mutex))
69 drm_log("error waiting on fence condition");
70 continue;
71 }
72
73 struct drm_fence *fence =
74 list_first_entry(&timeline->pending_fences, struct drm_fence, node);
75 int ret;
76
77 mtx_unlock(&timeline->fence_mutex);
78 ret = poll(&(struct pollfd){fence->fd, POLLIN}, 1, -1); /* wait forever */
79 mtx_lock(&timeline->fence_mutex);
80
81 if (ret == 1) {
82 drm_dbg("fence signaled: %p (%" PRIu64 ")", fence, fence->fence_id);
83 timeline->vctx->fence_retire(timeline->vctx, timeline->ring_idx,
84 fence->fence_id);
85 write_eventfd(timeline->eventfd, 1);
86 drm_fence_destroy(fence);
87 } else if (ret != 0) {
88 drm_log("poll failed: %s", strerror(errno));
89 }
90 }
91 mtx_unlock(&timeline->fence_mutex);
92
93 return 0;
94 }
95
96 void
drm_timeline_init(struct drm_timeline * timeline,struct virgl_context * vctx,const char * name,int eventfd,int ring_idx)97 drm_timeline_init(struct drm_timeline *timeline, struct virgl_context *vctx,
98 const char *name, int eventfd, int ring_idx)
99 {
100 timeline->vctx = vctx;
101 timeline->name = name;
102 timeline->eventfd = eventfd;
103 timeline->ring_idx = ring_idx;
104
105 timeline->last_fence_fd = -1;
106
107 list_inithead(&timeline->pending_fences);
108
109 mtx_init(&timeline->fence_mutex, mtx_plain);
110 cnd_init(&timeline->fence_cond);
111
112 timeline->sync_thread = u_thread_create(thread_sync, timeline);
113 }
114
115 void
drm_timeline_fini(struct drm_timeline * timeline)116 drm_timeline_fini(struct drm_timeline *timeline)
117 {
118 /* signal thread_sync to shutdown: */
119 mtx_lock(&timeline->fence_mutex);
120 timeline->stop_sync_thread = true;
121 cnd_signal(&timeline->fence_cond);
122 mtx_unlock(&timeline->fence_mutex);
123
124 /* wait for thread_sync to exit: */
125 thrd_join(timeline->sync_thread, NULL);
126
127 if (timeline->last_fence_fd != -1)
128 close(timeline->last_fence_fd);
129
130 /* cleanup remaining fences: */
131 list_for_each_entry_safe (struct drm_fence, fence, &timeline->pending_fences, node) {
132 drm_fence_destroy(fence);
133 }
134
135 cnd_destroy(&timeline->fence_cond);
136 mtx_destroy(&timeline->fence_mutex);
137 }
138
139 int
drm_timeline_submit_fence(struct drm_timeline * timeline,uint32_t flags,uint64_t fence_id)140 drm_timeline_submit_fence(struct drm_timeline *timeline, uint32_t flags,
141 uint64_t fence_id)
142 {
143 if (timeline->last_fence_fd == -1)
144 return -EINVAL;
145
146 struct drm_fence *fence =
147 drm_fence_create(timeline->last_fence_fd, flags, fence_id);
148
149 if (!fence)
150 return -ENOMEM;
151
152 drm_dbg("fence: %p (%" PRIu64 ")", fence, fence->fence_id);
153
154 mtx_lock(&timeline->fence_mutex);
155 list_addtail(&fence->node, &timeline->pending_fences);
156 cnd_signal(&timeline->fence_cond);
157 mtx_unlock(&timeline->fence_mutex);
158
159 return 0;
160 }
161
162 /* takes ownership of the fd */
163 void
drm_timeline_set_last_fence_fd(struct drm_timeline * timeline,int fd)164 drm_timeline_set_last_fence_fd(struct drm_timeline *timeline, int fd)
165 {
166 if (timeline->last_fence_fd != -1)
167 close(timeline->last_fence_fd);
168 timeline->last_fence_fd = fd;
169 }
170