1 /*
2 * Copyright © 2022 Google, Inc.
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 FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #include <assert.h>
25 #include <inttypes.h>
26 #include <pthread.h>
27
28 #include "util/libsync.h"
29 #include "util/os_file.h"
30
31 #include "drm/freedreno_ringbuffer_sp.h"
32 #include "virtio_priv.h"
33
34 static void
retire_execute(void * job,void * gdata,int thread_index)35 retire_execute(void *job, void *gdata, int thread_index)
36 {
37 struct fd_submit_sp *fd_submit = job;
38
39 sync_wait(fd_submit->out_fence_fd, -1);
40 close(fd_submit->out_fence_fd);
41 }
42
43 static void
retire_cleanup(void * job,void * gdata,int thread_index)44 retire_cleanup(void *job, void *gdata, int thread_index)
45 {
46 struct fd_submit_sp *fd_submit = job;
47 fd_submit_del(&fd_submit->base);
48 }
49
50 static int
flush_submit_list(struct list_head * submit_list)51 flush_submit_list(struct list_head *submit_list)
52 {
53 struct fd_submit_sp *fd_submit = to_fd_submit_sp(last_submit(submit_list));
54 struct virtio_pipe *virtio_pipe = to_virtio_pipe(fd_submit->base.pipe);
55 struct fd_device *dev = virtio_pipe->base.dev;
56
57 unsigned nr_cmds = 0;
58
59 /* Determine the number of extra cmds's from deferred submits that
60 * we will be merging in:
61 */
62 foreach_submit (submit, submit_list) {
63 assert(submit->pipe == &virtio_pipe->base);
64 nr_cmds += to_fd_ringbuffer_sp(submit->primary)->u.nr_cmds;
65 }
66
67 /* TODO we can get rid of the extra copy into the req by just
68 * assuming the max amount that nr->bos will grow is by the
69 * nr_cmds, and just over-allocate a bit.
70 */
71
72 struct drm_msm_gem_submit_cmd cmds[nr_cmds];
73
74 unsigned cmd_idx = 0;
75
76 /* Build up the table of cmds, and for all but the last submit in the
77 * list, merge their bo tables into the last submit.
78 */
79 foreach_submit_safe (submit, submit_list) {
80 struct fd_ringbuffer_sp *deferred_primary =
81 to_fd_ringbuffer_sp(submit->primary);
82
83 for (unsigned i = 0; i < deferred_primary->u.nr_cmds; i++) {
84 cmds[cmd_idx].type = MSM_SUBMIT_CMD_BUF;
85 cmds[cmd_idx].submit_idx =
86 fd_submit_append_bo(fd_submit, deferred_primary->u.cmds[i].ring_bo);
87 cmds[cmd_idx].submit_offset = deferred_primary->offset;
88 cmds[cmd_idx].size = deferred_primary->u.cmds[i].size;
89 cmds[cmd_idx].pad = 0;
90 cmds[cmd_idx].nr_relocs = 0;
91
92 cmd_idx++;
93 }
94
95 /* We are merging all the submits in the list into the last submit,
96 * so the remainder of the loop body doesn't apply to the last submit
97 */
98 if (submit == last_submit(submit_list)) {
99 DEBUG_MSG("merged %u submits", cmd_idx);
100 break;
101 }
102
103 struct fd_submit_sp *fd_deferred_submit = to_fd_submit_sp(submit);
104 for (unsigned i = 0; i < fd_deferred_submit->nr_bos; i++) {
105 /* Note: if bo is used in both the current submit and the deferred
106 * submit being merged, we expect to hit the fast-path as we add it
107 * to the current submit:
108 */
109 fd_submit_append_bo(fd_submit, fd_deferred_submit->bos[i]);
110 }
111
112 /* Now that the cmds/bos have been transfered over to the current submit,
113 * we can remove the deferred submit from the list and drop it's reference
114 */
115 list_del(&submit->node);
116 fd_submit_del(submit);
117 }
118
119 /* Needs to be after get_cmd() as that could create bos/cmds table:
120 *
121 * NOTE allocate on-stack in the common case, but with an upper-
122 * bound to limit on-stack allocation to 4k:
123 */
124 const unsigned bo_limit = 4096 / sizeof(struct drm_msm_gem_submit_bo);
125 bool bos_on_stack = fd_submit->nr_bos < bo_limit;
126 struct drm_msm_gem_submit_bo
127 _submit_bos[bos_on_stack ? fd_submit->nr_bos : 0];
128 struct drm_msm_gem_submit_bo *submit_bos;
129 uint32_t _guest_handles[bos_on_stack ? fd_submit->nr_bos : 0];
130 uint32_t *guest_handles;
131 if (bos_on_stack) {
132 submit_bos = _submit_bos;
133 guest_handles = _guest_handles;
134 } else {
135 submit_bos = malloc(fd_submit->nr_bos * sizeof(submit_bos[0]));
136 guest_handles = malloc(fd_submit->nr_bos * sizeof(guest_handles[0]));
137 }
138
139 for (unsigned i = 0; i < fd_submit->nr_bos; i++) {
140 struct virtio_bo *virtio_bo = to_virtio_bo(fd_submit->bos[i]);
141
142 guest_handles[i] = virtio_bo->base.handle;
143
144 submit_bos[i].flags = fd_submit->bos[i]->reloc_flags;
145 submit_bos[i].handle = virtio_bo->res_id;
146 submit_bos[i].presumed = 0;
147 }
148
149 if (virtio_pipe->next_submit_fence <= 0)
150 virtio_pipe->next_submit_fence = 1;
151
152 uint32_t kfence = virtio_pipe->next_submit_fence++;
153
154 /* TODO avoid extra memcpy, and populate bo's and cmds directly
155 * into the req msg
156 */
157 unsigned bos_len = fd_submit->nr_bos * sizeof(struct drm_msm_gem_submit_bo);
158 unsigned cmd_len = nr_cmds * sizeof(struct drm_msm_gem_submit_cmd);
159 unsigned req_len = sizeof(struct msm_ccmd_gem_submit_req) + bos_len + cmd_len;
160 struct msm_ccmd_gem_submit_req *req = malloc(req_len);
161
162 req->hdr = MSM_CCMD(GEM_SUBMIT, req_len);
163 req->flags = virtio_pipe->pipe;
164 req->queue_id = virtio_pipe->queue_id;
165 req->nr_bos = fd_submit->nr_bos;
166 req->nr_cmds = nr_cmds;
167 req->fence = kfence;
168
169 memcpy(req->payload, submit_bos, bos_len);
170 memcpy(req->payload + bos_len, cmds, cmd_len);
171
172 struct fd_submit_fence *out_fence = fd_submit->out_fence;
173 int *out_fence_fd = NULL;
174
175 if (out_fence) {
176 out_fence->fence.kfence = kfence;
177 out_fence->fence.ufence = fd_submit->base.fence;
178 /* Even if gallium driver hasn't requested a fence-fd, request one.
179 * This way, if we have to block waiting for the fence, we can do
180 * it in the guest, rather than in the single-threaded host.
181 */
182 out_fence->use_fence_fd = true;
183 out_fence_fd = &out_fence->fence_fd;
184 } else {
185 /* we are using retire_queue, so we need an out-fence for each
186 * submit.. we can just re-use fd_submit->out_fence_fd for temporary
187 * storage.
188 */
189 out_fence_fd = &fd_submit->out_fence_fd;
190 }
191
192 if (fd_submit->in_fence_fd != -1) {
193 virtio_pipe->no_implicit_sync = true;
194 }
195
196 if (virtio_pipe->no_implicit_sync) {
197 req->flags |= MSM_SUBMIT_NO_IMPLICIT;
198 }
199
200 virtio_execbuf_fenced(dev, &req->hdr, guest_handles, req->nr_bos,
201 fd_submit->in_fence_fd, out_fence_fd,
202 virtio_pipe->ring_idx);
203
204 free(req);
205
206 if (!bos_on_stack) {
207 free(submit_bos);
208 free(guest_handles);
209 }
210
211 if (fd_submit->in_fence_fd != -1)
212 close(fd_submit->in_fence_fd);
213
214 if (out_fence_fd != &fd_submit->out_fence_fd)
215 fd_submit->out_fence_fd = os_dupfd_cloexec(*out_fence_fd);
216
217 fd_submit_ref(&fd_submit->base);
218
219 util_queue_fence_init(&fd_submit->retire_fence);
220
221 util_queue_add_job(&virtio_pipe->retire_queue,
222 fd_submit, &fd_submit->retire_fence,
223 retire_execute,
224 retire_cleanup,
225 0);
226
227 return 0;
228 }
229
230 struct fd_submit *
virtio_submit_new(struct fd_pipe * pipe)231 virtio_submit_new(struct fd_pipe *pipe)
232 {
233 /* We don't do any translation from internal FD_RELOC flags to MSM flags. */
234 STATIC_ASSERT(FD_RELOC_READ == MSM_SUBMIT_BO_READ);
235 STATIC_ASSERT(FD_RELOC_WRITE == MSM_SUBMIT_BO_WRITE);
236 STATIC_ASSERT(FD_RELOC_DUMP == MSM_SUBMIT_BO_DUMP);
237
238 return fd_submit_sp_new(pipe, flush_submit_list);
239 }
240