1 /*
2 * Copyright © 2022 Google, Inc.
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "util/libsync.h"
7
8 #include "virtio_priv.h"
9
10 static void *
virtio_bo_mmap(struct fd_bo * bo)11 virtio_bo_mmap(struct fd_bo *bo)
12 {
13 struct vdrm_device *vdrm = to_virtio_device(bo->dev)->vdrm;
14 struct virtio_bo *virtio_bo = to_virtio_bo(bo);
15
16 /* If we have uploaded, we need to wait for host to handle that
17 * before we can allow guest-side CPU access:
18 */
19 if (virtio_bo->has_upload_seqno) {
20
21 virtio_bo->has_upload_seqno = false;
22
23 vdrm_flush(vdrm);
24 vdrm_host_sync(vdrm, &(struct vdrm_ccmd_req) {
25 .seqno = virtio_bo->upload_seqno,
26 });
27 }
28
29 return vdrm_bo_map(vdrm, bo->handle, bo->size, NULL);
30 }
31
32 static int
virtio_bo_cpu_prep(struct fd_bo * bo,struct fd_pipe * pipe,uint32_t op)33 virtio_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
34 {
35 MESA_TRACE_FUNC();
36 struct vdrm_device *vdrm = to_virtio_device(bo->dev)->vdrm;
37 int ret;
38
39 /*
40 * Wait first in the guest, to avoid a blocking call in host.
41 * If implicit sync it used, we still need to *also* wait in
42 * host, if it is a shared buffer, because the guest doesn't
43 * know about usage of the bo in the host (or other guests).
44 */
45
46 ret = vdrm_bo_wait(vdrm, bo->handle);
47 if (ret)
48 goto out;
49
50 /*
51 * The buffer could be shared with other things on the host side
52 * so have to poll the host. But we only get here with the shared
53 * buffers plus implicit sync. Hopefully that is rare enough.
54 */
55
56 struct msm_ccmd_gem_cpu_prep_req req = {
57 .hdr = MSM_CCMD(GEM_CPU_PREP, sizeof(req)),
58 .res_id = to_virtio_bo(bo)->res_id,
59 .op = op,
60 };
61 struct msm_ccmd_gem_cpu_prep_rsp *rsp;
62
63 /* We can't do a blocking wait in the host, so we have to poll: */
64 do {
65 rsp = vdrm_alloc_rsp(vdrm, &req.hdr, sizeof(*rsp));
66
67 ret = vdrm_send_req(vdrm, &req.hdr, true);
68 if (ret)
69 goto out;
70
71 ret = rsp->ret;
72 } while (ret == -EBUSY);
73
74 out:
75 return ret;
76 }
77
78 static int
virtio_bo_madvise(struct fd_bo * bo,int willneed)79 virtio_bo_madvise(struct fd_bo *bo, int willneed)
80 {
81 /* TODO:
82 * Currently unsupported, synchronous WILLNEED calls would introduce too
83 * much latency.. ideally we'd keep state in the guest and only flush
84 * down to host when host is under memory pressure. (Perhaps virtio-balloon
85 * could signal this?)
86 */
87 return willneed;
88 }
89
90 static uint64_t
virtio_bo_iova(struct fd_bo * bo)91 virtio_bo_iova(struct fd_bo *bo)
92 {
93 /* The shmem bo is allowed to have no iova, as it is only used for
94 * guest<->host communications:
95 */
96 assert(bo->iova || (to_virtio_bo(bo)->blob_id == 0));
97 return bo->iova;
98 }
99
100 static void
virtio_bo_set_name(struct fd_bo * bo,const char * fmt,va_list ap)101 virtio_bo_set_name(struct fd_bo *bo, const char *fmt, va_list ap)
102 {
103 char name[32];
104 int sz;
105
106 /* Note, we cannot set name on the host for the shmem bo, as
107 * that isn't a real gem obj on the host side.. not having
108 * an iova is a convenient way to detect this case:
109 */
110 if (!bo->iova)
111 return;
112
113 sz = vsnprintf(name, sizeof(name), fmt, ap);
114 sz = MIN2(sz, sizeof(name));
115
116 unsigned req_len = sizeof(struct msm_ccmd_gem_set_name_req) + align(sz, 4);
117
118 uint8_t buf[req_len];
119 struct msm_ccmd_gem_set_name_req *req = (void *)buf;
120
121 req->hdr = MSM_CCMD(GEM_SET_NAME, req_len);
122 req->res_id = to_virtio_bo(bo)->res_id;
123 req->len = sz;
124
125 memcpy(req->payload, name, sz);
126
127 vdrm_send_req(to_virtio_device(bo->dev)->vdrm, &req->hdr, false);
128 }
129
130 static int
virtio_bo_dmabuf(struct fd_bo * bo)131 virtio_bo_dmabuf(struct fd_bo *bo)
132 {
133 struct virtio_device *virtio_dev = to_virtio_device(bo->dev);
134
135 return vdrm_bo_export_dmabuf(virtio_dev->vdrm, bo->handle);
136 }
137
138 static void
bo_upload(struct fd_bo * bo,unsigned off,void * src,unsigned len)139 bo_upload(struct fd_bo *bo, unsigned off, void *src, unsigned len)
140 {
141 MESA_TRACE_FUNC();
142 unsigned req_len = sizeof(struct msm_ccmd_gem_upload_req) + align(len, 4);
143 struct virtio_bo *virtio_bo = to_virtio_bo(bo);
144
145 uint8_t buf[req_len];
146 struct msm_ccmd_gem_upload_req *req = (void *)buf;
147
148 req->hdr = MSM_CCMD(GEM_UPLOAD, req_len);
149 req->res_id = virtio_bo->res_id;
150 req->pad = 0;
151 req->off = off;
152 req->len = len;
153
154 memcpy(req->payload, src, len);
155
156 vdrm_send_req(to_virtio_device(bo->dev)->vdrm, &req->hdr, false);
157
158 virtio_bo->upload_seqno = req->hdr.seqno;
159 virtio_bo->has_upload_seqno = true;
160 }
161
162 static void
virtio_bo_upload(struct fd_bo * bo,void * src,unsigned off,unsigned len)163 virtio_bo_upload(struct fd_bo *bo, void *src, unsigned off, unsigned len)
164 {
165 while (len > 0) {
166 unsigned sz = MIN2(len, 0x1000);
167 bo_upload(bo, off, src, sz);
168 off += sz;
169 src += sz;
170 len -= sz;
171 }
172 }
173
174 /**
175 * For recently allocated buffers, an immediate mmap would stall waiting
176 * for the host to handle the allocation and map to the guest, which
177 * could take a few ms. So for small transfers to recently allocated
178 * buffers, we'd prefer to use the upload path instead.
179 */
180 static bool
virtio_bo_prefer_upload(struct fd_bo * bo,unsigned len)181 virtio_bo_prefer_upload(struct fd_bo *bo, unsigned len)
182 {
183 struct virtio_bo *virtio_bo = to_virtio_bo(bo);
184
185 /* If we've already taken the hit of mmap'ing the buffer, then no reason
186 * to take the upload path:
187 */
188 if (bo->map)
189 return false;
190
191 if (len > 0x4000)
192 return false;
193
194 int64_t age_ns = os_time_get_nano() - virtio_bo->alloc_time_ns;
195 if (age_ns > 5000000)
196 return false;
197
198 return true;
199 }
200
201 static void
set_iova(struct fd_bo * bo,uint64_t iova)202 set_iova(struct fd_bo *bo, uint64_t iova)
203 {
204 struct msm_ccmd_gem_set_iova_req req = {
205 .hdr = MSM_CCMD(GEM_SET_IOVA, sizeof(req)),
206 .res_id = to_virtio_bo(bo)->res_id,
207 .iova = iova,
208 };
209
210 vdrm_send_req(to_virtio_device(bo->dev)->vdrm, &req.hdr, false);
211 }
212
213 static void
virtio_bo_finalize(struct fd_bo * bo)214 virtio_bo_finalize(struct fd_bo *bo)
215 {
216 /* Release iova by setting to zero: */
217 if (bo->iova) {
218 set_iova(bo, 0);
219
220 virtio_dev_free_iova(bo->dev, bo->iova, bo->size);
221 }
222 }
223
224 static const struct fd_bo_funcs funcs = {
225 .map = virtio_bo_mmap,
226 .cpu_prep = virtio_bo_cpu_prep,
227 .madvise = virtio_bo_madvise,
228 .iova = virtio_bo_iova,
229 .set_name = virtio_bo_set_name,
230 .dmabuf = virtio_bo_dmabuf,
231 .upload = virtio_bo_upload,
232 .prefer_upload = virtio_bo_prefer_upload,
233 .finalize = virtio_bo_finalize,
234 .destroy = fd_bo_fini_common,
235 };
236
237 static struct fd_bo *
bo_from_handle(struct fd_device * dev,uint32_t size,uint32_t handle)238 bo_from_handle(struct fd_device *dev, uint32_t size, uint32_t handle)
239 {
240 struct virtio_device *virtio_dev = to_virtio_device(dev);
241 struct virtio_bo *virtio_bo;
242 struct fd_bo *bo;
243
244 virtio_bo = calloc(1, sizeof(*virtio_bo));
245 if (!virtio_bo)
246 return NULL;
247
248 virtio_bo->alloc_time_ns = os_time_get_nano();
249
250 bo = &virtio_bo->base;
251
252 /* Note we need to set these because allocation_wait_execute() could
253 * run before bo_init_commont():
254 */
255 bo->dev = dev;
256 p_atomic_set(&bo->refcnt, 1);
257
258 bo->size = size;
259 bo->funcs = &funcs;
260 bo->handle = handle;
261
262 /* Don't assume we can mmap an imported bo: */
263 bo->alloc_flags = FD_BO_NOMAP;
264
265 virtio_bo->res_id = vdrm_handle_to_res_id(virtio_dev->vdrm, handle);
266
267 fd_bo_init_common(bo, dev);
268
269 return bo;
270 }
271
272 /* allocate a new buffer object from existing handle */
273 struct fd_bo *
virtio_bo_from_handle(struct fd_device * dev,uint32_t size,uint32_t handle)274 virtio_bo_from_handle(struct fd_device *dev, uint32_t size, uint32_t handle)
275 {
276 struct fd_bo *bo = bo_from_handle(dev, size, handle);
277
278 if (!bo)
279 return NULL;
280
281 bo->iova = virtio_dev_alloc_iova(dev, size);
282 if (!bo->iova)
283 goto fail;
284
285 set_iova(bo, bo->iova);
286
287 return bo;
288
289 fail:
290 virtio_bo_finalize(bo);
291 fd_bo_fini_common(bo);
292 return NULL;
293 }
294
295 /* allocate a buffer object: */
296 struct fd_bo *
virtio_bo_new(struct fd_device * dev,uint32_t size,uint32_t flags)297 virtio_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
298 {
299 struct virtio_device *virtio_dev = to_virtio_device(dev);
300 struct msm_ccmd_gem_new_req req = {
301 .hdr = MSM_CCMD(GEM_NEW, sizeof(req)),
302 .size = size,
303 };
304
305 if (flags & FD_BO_SCANOUT)
306 req.flags |= MSM_BO_SCANOUT;
307
308 if (flags & FD_BO_GPUREADONLY)
309 req.flags |= MSM_BO_GPU_READONLY;
310
311 if (flags & FD_BO_CACHED_COHERENT) {
312 req.flags |= MSM_BO_CACHED_COHERENT;
313 } else {
314 req.flags |= MSM_BO_WC;
315 }
316
317 uint32_t blob_flags = 0;
318 if (flags & (FD_BO_SHARED | FD_BO_SCANOUT)) {
319 blob_flags = VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE |
320 VIRTGPU_BLOB_FLAG_USE_SHAREABLE;
321 }
322
323 if (!(flags & FD_BO_NOMAP)) {
324 blob_flags |= VIRTGPU_BLOB_FLAG_USE_MAPPABLE;
325 }
326
327 uint32_t blob_id = p_atomic_inc_return(&virtio_dev->next_blob_id);
328
329 /* tunneled cmds are processed separately on host side,
330 * before the renderer->get_blob() callback.. the blob_id
331 * is used to link the created bo to the get_blob() call
332 */
333 req.blob_id = blob_id;
334 req.iova = virtio_dev_alloc_iova(dev, size);
335 if (!req.iova)
336 goto fail;
337
338 uint32_t handle =
339 vdrm_bo_create(virtio_dev->vdrm, size, blob_flags, blob_id, &req.hdr);
340 if (!handle)
341 goto fail;
342
343 struct fd_bo *bo = bo_from_handle(dev, size, handle);
344 struct virtio_bo *virtio_bo = to_virtio_bo(bo);
345
346 virtio_bo->blob_id = blob_id;
347 bo->iova = req.iova;
348
349 return bo;
350
351 fail:
352 if (req.iova)
353 virtio_dev_free_iova(dev, req.iova, size);
354 return NULL;
355 }
356