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 "util/libsync.h"
25
26 #include "virtio_priv.h"
27
28 static int
bo_allocate(struct virtio_bo * virtio_bo)29 bo_allocate(struct virtio_bo *virtio_bo)
30 {
31 struct fd_bo *bo = &virtio_bo->base;
32 if (!virtio_bo->offset) {
33 struct drm_virtgpu_map req = {
34 .handle = bo->handle,
35 };
36 int ret;
37
38 ret = drmIoctl(bo->dev->fd, DRM_IOCTL_VIRTGPU_MAP, &req);
39 if (ret) {
40 ERROR_MSG("alloc failed: %s", strerror(errno));
41 return ret;
42 }
43
44 virtio_bo->offset = req.offset;
45 }
46
47 return 0;
48 }
49
50 static int
virtio_bo_offset(struct fd_bo * bo,uint64_t * offset)51 virtio_bo_offset(struct fd_bo *bo, uint64_t *offset)
52 {
53 struct virtio_bo *virtio_bo = to_virtio_bo(bo);
54 int ret = bo_allocate(virtio_bo);
55 if (ret)
56 return ret;
57 *offset = virtio_bo->offset;
58 return 0;
59 }
60
61 static int
virtio_bo_cpu_prep_guest(struct fd_bo * bo)62 virtio_bo_cpu_prep_guest(struct fd_bo *bo)
63 {
64 struct drm_virtgpu_3d_wait args = {
65 .handle = bo->handle,
66 };
67 int ret;
68
69 /* Side note, this ioctl is defined as IO_WR but should be IO_W: */
70 ret = drmIoctl(bo->dev->fd, DRM_IOCTL_VIRTGPU_WAIT, &args);
71 if (ret && errno == EBUSY)
72 return -EBUSY;
73
74 return 0;
75 }
76
77 static int
virtio_bo_cpu_prep(struct fd_bo * bo,struct fd_pipe * pipe,uint32_t op)78 virtio_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
79 {
80 int ret;
81
82 /*
83 * Wait first in the guest, to avoid a blocking call in host.
84 * If implicit sync it used, we still need to *also* wait in
85 * host, if it is a shared buffer, because the guest doesn't
86 * know about usage of the bo in the host (or other guests).
87 */
88
89 ret = virtio_bo_cpu_prep_guest(bo);
90 if (ret)
91 goto out;
92
93 /* If buffer is not shared, then it is not shared with host,
94 * so we don't need to worry about implicit sync in host:
95 */
96 if (!bo->shared)
97 goto out;
98
99 /* If buffer is shared, but we are using explicit sync, no
100 * need to fallback to implicit sync in host:
101 */
102 if (pipe && to_virtio_pipe(pipe)->no_implicit_sync)
103 goto out;
104
105 struct msm_ccmd_gem_cpu_prep_req req = {
106 .hdr = MSM_CCMD(GEM_CPU_PREP, sizeof(req)),
107 .res_id = to_virtio_bo(bo)->res_id,
108 .op = op,
109 };
110 struct msm_ccmd_gem_cpu_prep_rsp *rsp;
111
112 /* We can't do a blocking wait in the host, so we have to poll: */
113 do {
114 rsp = virtio_alloc_rsp(bo->dev, &req.hdr, sizeof(*rsp));
115
116 ret = virtio_execbuf(bo->dev, &req.hdr, true);
117 if (ret)
118 goto out;
119
120 ret = rsp->ret;
121 } while (ret == -EBUSY);
122
123 out:
124 return ret;
125 }
126
127 static void
virtio_bo_cpu_fini(struct fd_bo * bo)128 virtio_bo_cpu_fini(struct fd_bo *bo)
129 {
130 /* no-op */
131 }
132
133 static int
virtio_bo_madvise(struct fd_bo * bo,int willneed)134 virtio_bo_madvise(struct fd_bo *bo, int willneed)
135 {
136 /* TODO:
137 * Currently unsupported, synchronous WILLNEED calls would introduce too
138 * much latency.. ideally we'd keep state in the guest and only flush
139 * down to host when host is under memory pressure. (Perhaps virtio-balloon
140 * could signal this?)
141 */
142 return willneed;
143 }
144
145 static uint64_t
virtio_bo_iova(struct fd_bo * bo)146 virtio_bo_iova(struct fd_bo *bo)
147 {
148 /* The shmem bo is allowed to have no iova, as it is only used for
149 * guest<->host communications:
150 */
151 assert(bo->iova || (to_virtio_bo(bo)->blob_id == 0));
152 return bo->iova;
153 }
154
155 static void
virtio_bo_set_name(struct fd_bo * bo,const char * fmt,va_list ap)156 virtio_bo_set_name(struct fd_bo *bo, const char *fmt, va_list ap)
157 {
158 char name[32];
159 int sz;
160
161 /* Note, we cannot set name on the host for the shmem bo, as
162 * that isn't a real gem obj on the host side.. not having
163 * an iova is a convenient way to detect this case:
164 */
165 if (!bo->iova)
166 return;
167
168 sz = vsnprintf(name, sizeof(name), fmt, ap);
169 sz = MIN2(sz, sizeof(name));
170
171 unsigned req_len = sizeof(struct msm_ccmd_gem_set_name_req) + align(sz, 4);
172
173 uint8_t buf[req_len];
174 struct msm_ccmd_gem_set_name_req *req = (void *)buf;
175
176 req->hdr = MSM_CCMD(GEM_SET_NAME, req_len);
177 req->res_id = to_virtio_bo(bo)->res_id;
178 req->len = sz;
179
180 memcpy(req->payload, name, sz);
181
182 virtio_execbuf(bo->dev, &req->hdr, false);
183 }
184
185 static void
bo_upload(struct fd_bo * bo,unsigned off,void * src,unsigned len)186 bo_upload(struct fd_bo *bo, unsigned off, void *src, unsigned len)
187 {
188 unsigned req_len = sizeof(struct msm_ccmd_gem_upload_req) + align(len, 4);
189
190 uint8_t buf[req_len];
191 struct msm_ccmd_gem_upload_req *req = (void *)buf;
192
193 req->hdr = MSM_CCMD(GEM_UPLOAD, req_len);
194 req->res_id = to_virtio_bo(bo)->res_id;
195 req->pad = 0;
196 req->off = off;
197 req->len = len;
198
199 memcpy(req->payload, src, len);
200
201 virtio_execbuf(bo->dev, &req->hdr, false);
202 }
203
204 static void
virtio_bo_upload(struct fd_bo * bo,void * src,unsigned len)205 virtio_bo_upload(struct fd_bo *bo, void *src, unsigned len)
206 {
207 unsigned off = 0;
208 while (len > 0) {
209 unsigned sz = MIN2(len, 0x1000);
210 bo_upload(bo, off, src, sz);
211 off += sz;
212 src += sz;
213 len -= sz;
214 }
215 }
216
217 static void
set_iova(struct fd_bo * bo,uint64_t iova)218 set_iova(struct fd_bo *bo, uint64_t iova)
219 {
220 struct msm_ccmd_gem_set_iova_req req = {
221 .hdr = MSM_CCMD(GEM_SET_IOVA, sizeof(req)),
222 .res_id = to_virtio_bo(bo)->res_id,
223 .iova = iova,
224 };
225
226 virtio_execbuf(bo->dev, &req.hdr, false);
227 }
228
229 static void
virtio_bo_destroy(struct fd_bo * bo)230 virtio_bo_destroy(struct fd_bo *bo)
231 {
232 struct virtio_bo *virtio_bo = to_virtio_bo(bo);
233
234 /* Release iova by setting to zero: */
235 if (bo->iova) {
236 set_iova(bo, 0);
237
238 virtio_dev_free_iova(bo->dev, bo->iova, bo->size);
239
240 /* Need to flush batched ccmds to ensure the host sees the iova
241 * release before the GEM handle is closed (ie. detach_resource()
242 * on the host side)
243 */
244 virtio_execbuf_flush(bo->dev);
245 }
246
247 free(virtio_bo);
248 }
249
250 static const struct fd_bo_funcs funcs = {
251 .offset = virtio_bo_offset,
252 .cpu_prep = virtio_bo_cpu_prep,
253 .cpu_fini = virtio_bo_cpu_fini,
254 .madvise = virtio_bo_madvise,
255 .iova = virtio_bo_iova,
256 .set_name = virtio_bo_set_name,
257 .upload = virtio_bo_upload,
258 .destroy = virtio_bo_destroy,
259 };
260
261 static struct fd_bo *
bo_from_handle(struct fd_device * dev,uint32_t size,uint32_t handle)262 bo_from_handle(struct fd_device *dev, uint32_t size, uint32_t handle)
263 {
264 struct virtio_bo *virtio_bo;
265 struct fd_bo *bo;
266
267 virtio_bo = calloc(1, sizeof(*virtio_bo));
268 if (!virtio_bo)
269 return NULL;
270
271 bo = &virtio_bo->base;
272
273 /* Note we need to set these because allocation_wait_execute() could
274 * run before bo_init_commont():
275 */
276 bo->dev = dev;
277 p_atomic_set(&bo->refcnt, 1);
278
279 bo->size = size;
280 bo->funcs = &funcs;
281 bo->handle = handle;
282
283 /* Don't assume we can mmap an imported bo: */
284 bo->alloc_flags = FD_BO_NOMAP;
285
286 struct drm_virtgpu_resource_info args = {
287 .bo_handle = handle,
288 };
289 int ret;
290
291 ret = drmCommandWriteRead(dev->fd, DRM_VIRTGPU_RESOURCE_INFO, &args, sizeof(args));
292 if (ret) {
293 INFO_MSG("failed to get resource info: %s", strerror(errno));
294 free(virtio_bo);
295 return NULL;
296 }
297
298 virtio_bo->res_id = args.res_handle;
299
300 fd_bo_init_common(bo, dev);
301
302 return bo;
303 }
304
305 /* allocate a new buffer object from existing handle */
306 struct fd_bo *
virtio_bo_from_handle(struct fd_device * dev,uint32_t size,uint32_t handle)307 virtio_bo_from_handle(struct fd_device *dev, uint32_t size, uint32_t handle)
308 {
309 struct fd_bo *bo = bo_from_handle(dev, size, handle);
310
311 if (!bo)
312 return NULL;
313
314 bo->iova = virtio_dev_alloc_iova(dev, size);
315 if (!bo->iova)
316 goto fail;
317
318 set_iova(bo, bo->iova);
319
320 return bo;
321
322 fail:
323 virtio_bo_destroy(bo);
324 return NULL;
325 }
326
327 /* allocate a buffer object: */
328 struct fd_bo *
virtio_bo_new(struct fd_device * dev,uint32_t size,uint32_t flags)329 virtio_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
330 {
331 struct virtio_device *virtio_dev = to_virtio_device(dev);
332 struct drm_virtgpu_resource_create_blob args = {
333 .blob_mem = VIRTGPU_BLOB_MEM_HOST3D,
334 .size = size,
335 };
336 struct msm_ccmd_gem_new_req req = {
337 .hdr = MSM_CCMD(GEM_NEW, sizeof(req)),
338 .size = size,
339 };
340 int ret;
341
342 if (flags & FD_BO_SCANOUT)
343 req.flags |= MSM_BO_SCANOUT;
344
345 if (flags & FD_BO_GPUREADONLY)
346 req.flags |= MSM_BO_GPU_READONLY;
347
348 if (flags & FD_BO_CACHED_COHERENT) {
349 req.flags |= MSM_BO_CACHED_COHERENT;
350 } else {
351 req.flags |= MSM_BO_WC;
352 }
353
354 if (flags & _FD_BO_VIRTIO_SHM) {
355 args.blob_id = 0;
356 args.blob_flags = VIRTGPU_BLOB_FLAG_USE_MAPPABLE;
357 } else {
358 if (flags & (FD_BO_SHARED | FD_BO_SCANOUT)) {
359 args.blob_flags = VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE |
360 VIRTGPU_BLOB_FLAG_USE_SHAREABLE;
361 }
362
363 if (!(flags & FD_BO_NOMAP)) {
364 args.blob_flags |= VIRTGPU_BLOB_FLAG_USE_MAPPABLE;
365 }
366
367 args.blob_id = p_atomic_inc_return(&virtio_dev->next_blob_id);
368 args.cmd = VOID2U64(&req);
369 args.cmd_size = sizeof(req);
370
371 /* tunneled cmds are processed separately on host side,
372 * before the renderer->get_blob() callback.. the blob_id
373 * is used to like the created bo to the get_blob() call
374 */
375 req.blob_id = args.blob_id;
376 req.iova = virtio_dev_alloc_iova(dev, size);
377 if (!req.iova) {
378 ret = -ENOMEM;
379 goto fail;
380 }
381 }
382
383 simple_mtx_lock(&virtio_dev->eb_lock);
384 if (args.cmd)
385 req.hdr.seqno = ++virtio_dev->next_seqno;
386 ret = drmIoctl(dev->fd, DRM_IOCTL_VIRTGPU_RESOURCE_CREATE_BLOB, &args);
387 simple_mtx_unlock(&virtio_dev->eb_lock);
388 if (ret)
389 goto fail;
390
391 struct fd_bo *bo = bo_from_handle(dev, size, args.bo_handle);
392 struct virtio_bo *virtio_bo = to_virtio_bo(bo);
393
394 virtio_bo->blob_id = args.blob_id;
395 bo->iova = req.iova;
396
397 return bo;
398
399 fail:
400 if (req.iova) {
401 virtio_dev_free_iova(dev, req.iova, size);
402 }
403 return NULL;
404 }
405