xref: /aosp_15_r20/external/mesa3d/src/freedreno/drm/virtio/virtio_bo.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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