mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git
synced 2026-10-28 21:02:43 +01:00
virtio_gpu_fence_alloc() can fail due to memory pressure and return NULL,
but its caller like virtio_gpu_init_submit() never checks it. Later,
virtio_gpu_init_submit() passes the NULL fence to
virtio_gpu_fence_event_create(), which unconditionally dereferences it.
Found when fuzzing the virtio driver with Syzkaller:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000012: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000090-0x0000000000000097]
CPU: 1 UID: 0 PID: 9991 Comm: syz.0.121 Not tainted 7.2.0 #4 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014
RIP: 0010:virtio_gpu_fence_event_create drivers/gpu/drm/virtio/virtgpu_submit.c:295 [inline]
RIP: 0010:virtio_gpu_init_submit drivers/gpu/drm/virtio/virtgpu_submit.c:398 [inline]
RIP: 0010:virtio_gpu_execbuffer_ioctl+0xc78/0x1aa0 drivers/gpu/drm/virtio/virtgpu_submit.c:505
Code: 85 ed 0f 85 21 09 00 00 e8 05 5a c9 fb 48 8b 44 24 10 48 8d b8 90 00 00 00 48 b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 9a 0d 00 00 48 8b 44 24 10 4c 89 b0 90 00 00 00
RSP: 0018:ffffc900039dfad0 EFLAGS: 00010216
RAX: dffffc0000000000 RBX: ffffc900039dfdd8 RCX: ffffffff85f6fd3d
RDX: 0000000000000012 RSI: ffffffff85f6fd4b RDI: 0000000000000090
RBP: 0000000000000000 R0virtio_gpu_virgl_process_cmd: ctrl 0x102, error 0x1203
R10: 0000000000000000 R11: 0000000000000000 R12: ffff8880132c4000
R13: 0000000000000000 R14: ffff888073b6c700 R15: 000000000000003b
FS: 00007fab480b96c0(0000) GS:ffff8880eb6e9000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007effbf5e55a8 CR3: 0000000048d19000 CR4: 0000000000350ef0
Call Trace:
<TASK>
drm_ioctl_kernel+0x1f4/0x3e0 drivers/gpu/drm/drm_ioctl.c:817
drm_ioctl+0x5f4/0xc70 drivers/gpu/drm/drm_ioctl.c:914
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl fs/ioctl.c:583 [inline]
__x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x116/0x800 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fab471a82bd
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fab480b9018 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007fab47435fa0 RCX: 00007fab471a82bd
RDX: 00002000000000c0 RSI: 00000000c0406442 RDI: 0000000000000003
RBP: 00007fab480b9080 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000001
R13: 00007fab47436038 R14: 00007fab47435fa0 R15: 00007ffe85ab0740
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:virtio_gpu_fence_event_create drivers/gpu/drm/virtio/virtgpu_submit.c:295 [inline]
RIP: 0010:virtio_gpu_init_submit drivers/gpu/drm/virtio/virtgpu_submit.c:398 [inline]
RIP: 0010:virtio_gpu_execbuffer_ioctl+0xc78/0x1aa0 drivers/gpu/drm/virtio/virtgpu_submit.c:505
Code: 85 ed 0f 85 21 09 00 00 e8 05 5a c9 fb 48 8b 44 24 10 48 8d b8 90 00 00 00 48 b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 9a 0d 00 00 48 8b 44 24 10 4c 89 b0 90 00 00 00
RSP: 0018:ffffc900039dfad0 EFLAGS: 00010216
RAX: dffffc0000000000 RBX: ffffc900039dfdd8 RCX: ffffffff85f6fd3d
RDX: 0000000000000012 RSI: ffffffff85f6fd4b RDI: 0000000000000090
RBP: 0000000000000000 R08: 0000000000000005 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: ffff8880132c4000
R13: 0000000000000000 R14: ffff888073b6c700 R15: 000000000000003b
FS: 00007fab480b96c0(0000) GS:ffff888098ae9000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f24c3759000 CR3: 0000000048d19000 CR4: 0000000000350ef0
----------------
Code disassembly (best guess):
0: 85 ed test %ebp,%ebp
2: 0f 85 21 09 00 00 jne 0x929
8: e8 05 5a c9 fb call 0xfbc95a12
d: 48 8b 44 24 10 mov 0x10(%rsp),%rax
12: 48 8d b8 90 00 00 00 lea 0x90(%rax),%rdi
19: 48 b8 00 00 00 00 00 movabs $0xdffffc0000000000,%rax
20: fc ff df
23: 48 89 fa mov %rdi,%rdx
26: 48 c1 ea 03 shr $0x3,%rdx
* 2a: 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1) <-- trapping instruction
2e: 0f 85 9a 0d 00 00 jne 0xdce
34: 48 8b 44 24 10 mov 0x10(%rsp),%rax
39: 4c 89 b0 90 00 00 00 mov %r14,0x90(%rax)
Fix by checking virtio_gpu_fence_alloc() in virtio_gpu_init_submit() and
returning -ENOMEM before any later code can dereference the NULL fence.
Fixes: 70d1ace56d ("drm/virtio: Conditionally allocate virtio_gpu_fence")
Cc: [email protected]
Signed-off-by: Peiyang He <[email protected]>
Assisted-by: Codex:gpt-5.5
Signed-off-by: Dmitry Osipenko <[email protected]>
Link: https://patch.msgid.link/00EFE4BA92889B14+20260909091114.2622550-1-peiyang_he@smail.nju.edu.cn
552 lines
12 KiB
C
552 lines
12 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright (C) 2015 Red Hat, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* Authors:
|
|
* Dave Airlie
|
|
* Alon Levy
|
|
*/
|
|
|
|
#include <linux/dma-fence-unwrap.h>
|
|
#include <linux/file.h>
|
|
#include <linux/sync_file.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include <drm/drm_file.h>
|
|
#include <drm/drm_syncobj.h>
|
|
#include <drm/virtgpu_drm.h>
|
|
|
|
#include "virtgpu_drv.h"
|
|
|
|
struct virtio_gpu_submit_post_dep {
|
|
struct drm_syncobj *syncobj;
|
|
struct dma_fence_chain *chain;
|
|
u64 point;
|
|
};
|
|
|
|
struct virtio_gpu_submit {
|
|
struct virtio_gpu_submit_post_dep *post_deps;
|
|
unsigned int num_out_syncobjs;
|
|
|
|
struct drm_syncobj **in_syncobjs;
|
|
unsigned int num_in_syncobjs;
|
|
|
|
struct virtio_gpu_object_array *buflist;
|
|
struct drm_virtgpu_execbuffer *exbuf;
|
|
struct virtio_gpu_fence *out_fence;
|
|
struct virtio_gpu_fpriv *vfpriv;
|
|
struct virtio_gpu_device *vgdev;
|
|
struct sync_file *sync_file;
|
|
struct drm_file *file;
|
|
int out_fence_fd;
|
|
u64 fence_ctx;
|
|
u32 ring_idx;
|
|
void *buf;
|
|
};
|
|
|
|
static int virtio_gpu_do_fence_wait(struct virtio_gpu_submit *submit,
|
|
struct dma_fence *in_fence)
|
|
{
|
|
u64 context = submit->fence_ctx + submit->ring_idx;
|
|
|
|
if (dma_fence_match_context(in_fence, context))
|
|
return 0;
|
|
|
|
return dma_fence_wait(in_fence, true);
|
|
}
|
|
|
|
static int virtio_gpu_dma_fence_wait(struct virtio_gpu_submit *submit,
|
|
struct dma_fence *fence)
|
|
{
|
|
struct dma_fence_unwrap itr;
|
|
struct dma_fence *f;
|
|
int err;
|
|
|
|
dma_fence_unwrap_for_each(f, &itr, fence) {
|
|
err = virtio_gpu_do_fence_wait(submit, f);
|
|
if (err) {
|
|
dma_fence_put(itr.chain);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void virtio_gpu_free_syncobjs(struct drm_syncobj **syncobjs,
|
|
u32 nr_syncobjs)
|
|
{
|
|
u32 i = nr_syncobjs;
|
|
|
|
while (i--) {
|
|
if (syncobjs[i])
|
|
drm_syncobj_put(syncobjs[i]);
|
|
}
|
|
|
|
kvfree(syncobjs);
|
|
}
|
|
|
|
static int
|
|
virtio_gpu_parse_deps(struct virtio_gpu_submit *submit)
|
|
{
|
|
struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
|
|
struct drm_virtgpu_execbuffer_syncobj syncobj_desc;
|
|
size_t syncobj_stride = exbuf->syncobj_stride;
|
|
u32 num_in_syncobjs = exbuf->num_in_syncobjs;
|
|
struct drm_syncobj **syncobjs;
|
|
int ret = 0, i;
|
|
|
|
if (!num_in_syncobjs)
|
|
return 0;
|
|
|
|
/*
|
|
* kvmalloc() at first tries to allocate memory using kmalloc() and
|
|
* falls back to vmalloc() only on failure. It also uses __GFP_NOWARN
|
|
* internally for allocations larger than a page size, preventing
|
|
* storm of KMSG warnings.
|
|
*/
|
|
syncobjs = kvzalloc_objs(*syncobjs, num_in_syncobjs);
|
|
if (!syncobjs)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < num_in_syncobjs; i++) {
|
|
u64 address = exbuf->in_syncobjs + i * syncobj_stride;
|
|
struct dma_fence *fence;
|
|
|
|
memset(&syncobj_desc, 0, sizeof(syncobj_desc));
|
|
|
|
if (copy_from_user(&syncobj_desc,
|
|
u64_to_user_ptr(address),
|
|
min(syncobj_stride, sizeof(syncobj_desc)))) {
|
|
ret = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
if (syncobj_desc.flags & ~VIRTGPU_EXECBUF_SYNCOBJ_FLAGS) {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
ret = drm_syncobj_find_fence(submit->file, syncobj_desc.handle,
|
|
syncobj_desc.point, 0, &fence);
|
|
if (ret)
|
|
break;
|
|
|
|
ret = virtio_gpu_dma_fence_wait(submit, fence);
|
|
|
|
dma_fence_put(fence);
|
|
if (ret)
|
|
break;
|
|
|
|
if (syncobj_desc.flags & VIRTGPU_EXECBUF_SYNCOBJ_RESET) {
|
|
syncobjs[i] = drm_syncobj_find(submit->file,
|
|
syncobj_desc.handle);
|
|
if (!syncobjs[i]) {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret) {
|
|
virtio_gpu_free_syncobjs(syncobjs, i);
|
|
return ret;
|
|
}
|
|
|
|
submit->num_in_syncobjs = num_in_syncobjs;
|
|
submit->in_syncobjs = syncobjs;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void virtio_gpu_reset_syncobjs(struct drm_syncobj **syncobjs,
|
|
u32 nr_syncobjs)
|
|
{
|
|
u32 i;
|
|
|
|
for (i = 0; i < nr_syncobjs; i++) {
|
|
if (syncobjs[i])
|
|
drm_syncobj_replace_fence(syncobjs[i], NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
virtio_gpu_free_post_deps(struct virtio_gpu_submit_post_dep *post_deps,
|
|
u32 nr_syncobjs)
|
|
{
|
|
u32 i = nr_syncobjs;
|
|
|
|
while (i--) {
|
|
kfree(post_deps[i].chain);
|
|
drm_syncobj_put(post_deps[i].syncobj);
|
|
}
|
|
|
|
kvfree(post_deps);
|
|
}
|
|
|
|
static int virtio_gpu_parse_post_deps(struct virtio_gpu_submit *submit)
|
|
{
|
|
struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
|
|
struct drm_virtgpu_execbuffer_syncobj syncobj_desc;
|
|
struct virtio_gpu_submit_post_dep *post_deps;
|
|
u32 num_out_syncobjs = exbuf->num_out_syncobjs;
|
|
size_t syncobj_stride = exbuf->syncobj_stride;
|
|
int ret = 0, i;
|
|
|
|
if (!num_out_syncobjs)
|
|
return 0;
|
|
|
|
post_deps = kvzalloc_objs(*post_deps, num_out_syncobjs);
|
|
if (!post_deps)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < num_out_syncobjs; i++) {
|
|
u64 address = exbuf->out_syncobjs + i * syncobj_stride;
|
|
|
|
memset(&syncobj_desc, 0, sizeof(syncobj_desc));
|
|
|
|
if (copy_from_user(&syncobj_desc,
|
|
u64_to_user_ptr(address),
|
|
min(syncobj_stride, sizeof(syncobj_desc)))) {
|
|
ret = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
post_deps[i].point = syncobj_desc.point;
|
|
|
|
if (syncobj_desc.flags) {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (syncobj_desc.point) {
|
|
post_deps[i].chain = dma_fence_chain_alloc();
|
|
if (!post_deps[i].chain) {
|
|
ret = -ENOMEM;
|
|
break;
|
|
}
|
|
}
|
|
|
|
post_deps[i].syncobj = drm_syncobj_find(submit->file,
|
|
syncobj_desc.handle);
|
|
if (!post_deps[i].syncobj) {
|
|
kfree(post_deps[i].chain);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret) {
|
|
virtio_gpu_free_post_deps(post_deps, i);
|
|
return ret;
|
|
}
|
|
|
|
submit->num_out_syncobjs = num_out_syncobjs;
|
|
submit->post_deps = post_deps;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
virtio_gpu_process_post_deps(struct virtio_gpu_submit *submit)
|
|
{
|
|
struct virtio_gpu_submit_post_dep *post_deps = submit->post_deps;
|
|
|
|
if (post_deps) {
|
|
struct dma_fence *fence = &submit->out_fence->f;
|
|
u32 i;
|
|
|
|
for (i = 0; i < submit->num_out_syncobjs; i++) {
|
|
if (post_deps[i].chain) {
|
|
drm_syncobj_add_point(post_deps[i].syncobj,
|
|
post_deps[i].chain,
|
|
fence, post_deps[i].point);
|
|
post_deps[i].chain = NULL;
|
|
} else {
|
|
drm_syncobj_replace_fence(post_deps[i].syncobj,
|
|
fence);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int virtio_gpu_fence_event_create(struct drm_device *dev,
|
|
struct drm_file *file,
|
|
struct virtio_gpu_fence *fence,
|
|
u32 ring_idx)
|
|
{
|
|
struct virtio_gpu_fence_event *e = NULL;
|
|
int ret;
|
|
|
|
e = kzalloc_obj(*e);
|
|
if (!e)
|
|
return -ENOMEM;
|
|
|
|
e->event.type = VIRTGPU_EVENT_FENCE_SIGNALED;
|
|
e->event.length = sizeof(e->event);
|
|
|
|
ret = drm_event_reserve_init(dev, file, &e->base, &e->event);
|
|
if (ret) {
|
|
kfree(e);
|
|
return ret;
|
|
}
|
|
|
|
fence->e = e;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int virtio_gpu_init_submit_buflist(struct virtio_gpu_submit *submit)
|
|
{
|
|
struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
|
|
u32 *bo_handles;
|
|
|
|
if (!exbuf->num_bo_handles)
|
|
return 0;
|
|
|
|
bo_handles = kvmalloc_array(exbuf->num_bo_handles, sizeof(*bo_handles),
|
|
GFP_KERNEL);
|
|
if (!bo_handles)
|
|
return -ENOMEM;
|
|
|
|
if (copy_from_user(bo_handles, u64_to_user_ptr(exbuf->bo_handles),
|
|
exbuf->num_bo_handles * sizeof(*bo_handles))) {
|
|
kvfree(bo_handles);
|
|
return -EFAULT;
|
|
}
|
|
|
|
submit->buflist = virtio_gpu_array_from_handles(submit->file, bo_handles,
|
|
exbuf->num_bo_handles);
|
|
if (!submit->buflist) {
|
|
kvfree(bo_handles);
|
|
return -ENOENT;
|
|
}
|
|
|
|
kvfree(bo_handles);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void virtio_gpu_cleanup_submit(struct virtio_gpu_submit *submit)
|
|
{
|
|
virtio_gpu_reset_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs);
|
|
virtio_gpu_free_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs);
|
|
virtio_gpu_free_post_deps(submit->post_deps, submit->num_out_syncobjs);
|
|
|
|
if (!IS_ERR(submit->buf))
|
|
kvfree(submit->buf);
|
|
|
|
if (submit->buflist)
|
|
virtio_gpu_array_put_free(submit->buflist);
|
|
|
|
if (submit->out_fence_fd >= 0)
|
|
put_unused_fd(submit->out_fence_fd);
|
|
|
|
if (submit->out_fence)
|
|
dma_fence_put(&submit->out_fence->f);
|
|
|
|
if (submit->sync_file)
|
|
fput(submit->sync_file->file);
|
|
}
|
|
|
|
static void virtio_gpu_submit(struct virtio_gpu_submit *submit)
|
|
{
|
|
virtio_gpu_cmd_submit(submit->vgdev, submit->buf, submit->exbuf->size,
|
|
submit->vfpriv->ctx_id, submit->buflist,
|
|
submit->out_fence);
|
|
virtio_gpu_notify(submit->vgdev);
|
|
}
|
|
|
|
static void virtio_gpu_complete_submit(struct virtio_gpu_submit *submit)
|
|
{
|
|
submit->buf = NULL;
|
|
submit->buflist = NULL;
|
|
submit->sync_file = NULL;
|
|
submit->out_fence_fd = -1;
|
|
}
|
|
|
|
static int virtio_gpu_init_submit(struct virtio_gpu_submit *submit,
|
|
struct drm_virtgpu_execbuffer *exbuf,
|
|
struct drm_device *dev,
|
|
struct drm_file *file,
|
|
u64 fence_ctx, u32 ring_idx)
|
|
{
|
|
struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
|
|
struct virtio_gpu_device *vgdev = dev->dev_private;
|
|
struct virtio_gpu_fence *out_fence;
|
|
bool drm_fence_event;
|
|
int err;
|
|
|
|
memset(submit, 0, sizeof(*submit));
|
|
|
|
if ((exbuf->flags & VIRTGPU_EXECBUF_RING_IDX) &&
|
|
(vfpriv->ring_idx_mask & BIT_ULL(ring_idx)))
|
|
drm_fence_event = true;
|
|
else
|
|
drm_fence_event = false;
|
|
|
|
if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) ||
|
|
exbuf->num_out_syncobjs ||
|
|
exbuf->num_bo_handles ||
|
|
drm_fence_event) {
|
|
out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx);
|
|
if (!out_fence)
|
|
return -ENOMEM;
|
|
} else {
|
|
out_fence = NULL;
|
|
}
|
|
|
|
if (drm_fence_event) {
|
|
err = virtio_gpu_fence_event_create(dev, file, out_fence, ring_idx);
|
|
if (err) {
|
|
dma_fence_put(&out_fence->f);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
submit->out_fence = out_fence;
|
|
submit->fence_ctx = fence_ctx;
|
|
submit->ring_idx = ring_idx;
|
|
submit->out_fence_fd = -1;
|
|
submit->vfpriv = vfpriv;
|
|
submit->vgdev = vgdev;
|
|
submit->exbuf = exbuf;
|
|
submit->file = file;
|
|
|
|
err = virtio_gpu_init_submit_buflist(submit);
|
|
if (err)
|
|
return err;
|
|
|
|
submit->buf = vmemdup_user(u64_to_user_ptr(exbuf->command), exbuf->size);
|
|
if (IS_ERR(submit->buf))
|
|
return PTR_ERR(submit->buf);
|
|
|
|
if (exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) {
|
|
err = get_unused_fd_flags(O_CLOEXEC);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
submit->out_fence_fd = err;
|
|
|
|
submit->sync_file = sync_file_create(&out_fence->f);
|
|
if (!submit->sync_file)
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int virtio_gpu_wait_in_fence(struct virtio_gpu_submit *submit)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (submit->exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_IN) {
|
|
struct dma_fence *in_fence =
|
|
sync_file_get_fence(submit->exbuf->fence_fd);
|
|
if (!in_fence)
|
|
return -EINVAL;
|
|
|
|
/*
|
|
* Wait if the fence is from a foreign context, or if the
|
|
* fence array contains any fence from a foreign context.
|
|
*/
|
|
ret = virtio_gpu_dma_fence_wait(submit, in_fence);
|
|
|
|
dma_fence_put(in_fence);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void virtio_gpu_install_out_fence_fd(struct virtio_gpu_submit *submit)
|
|
{
|
|
if (submit->sync_file) {
|
|
submit->exbuf->fence_fd = submit->out_fence_fd;
|
|
fd_install(submit->out_fence_fd, submit->sync_file->file);
|
|
}
|
|
}
|
|
|
|
static int virtio_gpu_lock_buflist(struct virtio_gpu_submit *submit)
|
|
{
|
|
if (submit->buflist)
|
|
return virtio_gpu_array_lock_resv(submit->buflist);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
|
|
struct drm_file *file)
|
|
{
|
|
struct virtio_gpu_device *vgdev = dev->dev_private;
|
|
struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
|
|
u64 fence_ctx = vgdev->fence_drv.context;
|
|
struct drm_virtgpu_execbuffer *exbuf = data;
|
|
struct virtio_gpu_submit submit;
|
|
u32 ring_idx = 0;
|
|
int ret = -EINVAL;
|
|
|
|
if (!vgdev->has_virgl_3d)
|
|
return -ENOSYS;
|
|
|
|
if (exbuf->flags & ~VIRTGPU_EXECBUF_FLAGS)
|
|
return ret;
|
|
|
|
if (exbuf->flags & VIRTGPU_EXECBUF_RING_IDX) {
|
|
if (exbuf->ring_idx >= vfpriv->num_rings)
|
|
return ret;
|
|
|
|
if (!vfpriv->base_fence_ctx)
|
|
return ret;
|
|
|
|
fence_ctx = vfpriv->base_fence_ctx;
|
|
ring_idx = exbuf->ring_idx;
|
|
}
|
|
|
|
virtio_gpu_create_context(dev, file);
|
|
|
|
ret = virtio_gpu_init_submit(&submit, exbuf, dev, file,
|
|
fence_ctx, ring_idx);
|
|
if (ret)
|
|
goto cleanup;
|
|
|
|
ret = virtio_gpu_parse_post_deps(&submit);
|
|
if (ret)
|
|
goto cleanup;
|
|
|
|
ret = virtio_gpu_parse_deps(&submit);
|
|
if (ret)
|
|
goto cleanup;
|
|
|
|
/*
|
|
* Await in-fences in the end of the job submission path to
|
|
* optimize the path by proceeding directly to the submission
|
|
* to virtio after the waits.
|
|
*/
|
|
ret = virtio_gpu_wait_in_fence(&submit);
|
|
if (ret)
|
|
goto cleanup;
|
|
|
|
ret = virtio_gpu_lock_buflist(&submit);
|
|
if (ret)
|
|
goto cleanup;
|
|
|
|
virtio_gpu_submit(&submit);
|
|
|
|
/*
|
|
* Set up user-out data after submitting the job to optimize
|
|
* the job submission path.
|
|
*/
|
|
virtio_gpu_install_out_fence_fd(&submit);
|
|
virtio_gpu_process_post_deps(&submit);
|
|
virtio_gpu_complete_submit(&submit);
|
|
cleanup:
|
|
if (ret && submit.out_fence && submit.out_fence->e) {
|
|
drm_event_cancel_free(dev, &submit.out_fence->e->base);
|
|
submit.out_fence->e = NULL;
|
|
}
|
|
virtio_gpu_cleanup_submit(&submit);
|
|
|
|
return ret;
|
|
}
|