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