xref: /aosp_15_r20/external/virglrenderer/src/drm/drm_fence.c (revision bbecb9d118dfdb95f99bd754f8fa9be01f189df3)
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