mesa/src/virtio/vdrm/vdrm.c

193 lines
3.7 KiB
C

/*
* Copyright © 2023 Google, Inc.
* SPDX-License-Identifier: MIT
*/
#include "util/u_math.h"
#include "util/perf/cpu_trace.h"
#include "vdrm.h"
struct vdrm_device * vdrm_virtgpu_connect(int fd, uint32_t context_type);
struct vdrm_device *
vdrm_device_connect(int fd, uint32_t context_type)
{
struct vdrm_device *vdev;
// TODO vtest vs virtio..
vdev = vdrm_virtgpu_connect(fd, context_type);
if (!vdev)
return NULL;
simple_mtx_init(&vdev->rsp_lock, mtx_plain);
simple_mtx_init(&vdev->eb_lock, mtx_plain);
return vdev;
}
void
vdrm_device_close(struct vdrm_device *vdev)
{
vdev->funcs->close(vdev);
free(vdev);
}
uint32_t
vdrm_bo_create(struct vdrm_device *vdev, size_t size, uint32_t blob_flags,
uint64_t blob_id, struct vdrm_ccmd_req *req)
{
uint32_t handle;
simple_mtx_lock(&vdev->eb_lock);
/* flush any buffered cmds so they are seen by the host *prior* to
* the cmds associated with bo creation.
*/
vdev->funcs->flush_locked(vdev, NULL);
req->seqno = ++vdev->next_seqno;
handle = vdev->funcs->bo_create(vdev, size, blob_flags, blob_id, req);
simple_mtx_unlock(&vdev->eb_lock);
return handle;
}
void *
vdrm_alloc_rsp(struct vdrm_device *vdev, struct vdrm_ccmd_req *req, uint32_t sz)
{
unsigned off;
simple_mtx_lock(&vdev->rsp_lock);
sz = align(sz, 8);
if ((vdev->next_rsp_off + sz) >= vdev->rsp_mem_len)
vdev->next_rsp_off = 0;
off = vdev->next_rsp_off;
vdev->next_rsp_off += sz;
simple_mtx_unlock(&vdev->rsp_lock);
req->rsp_off = off;
struct vdrm_ccmd_rsp *rsp = (void *)&vdev->rsp_mem[off];
rsp->len = sz;
return rsp;
}
static int
enqueue_req(struct vdrm_device *vdev, struct vdrm_ccmd_req *req)
{
simple_mtx_assert_locked(&vdev->eb_lock);
req->seqno = ++vdev->next_seqno;
if ((vdev->reqbuf_len + req->len) > sizeof(vdev->reqbuf)) {
int ret = vdev->funcs->flush_locked(vdev, NULL);
if (ret)
return ret;
}
memcpy(&vdev->reqbuf[vdev->reqbuf_len], req, req->len);
vdev->reqbuf_len += req->len;
vdev->reqbuf_cnt++;
return 0;
}
int
vdrm_execbuf(struct vdrm_device *vdev, struct vdrm_execbuf_params *p)
{
int ret = 0;
MESA_TRACE_FUNC();
simple_mtx_lock(&vdev->eb_lock);
p->req->seqno = ++vdev->next_seqno;
ret = vdev->funcs->flush_locked(vdev, NULL);
if (ret)
goto out_unlock;
ret = vdev->funcs->execbuf_locked(vdev, p, p->req, p->req->len);
out_unlock:
simple_mtx_unlock(&vdev->eb_lock);
return ret;
}
/**
* Buffer/send a request cmd to host
*/
int
vdrm_send_req(struct vdrm_device *vdev, struct vdrm_ccmd_req *req, bool sync)
{
MESA_TRACE_FUNC();
uintptr_t fence = 0;
int ret = 0;
simple_mtx_lock(&vdev->eb_lock);
ret = enqueue_req(vdev, req);
if (ret || !sync)
goto out_unlock;
ret = vdev->funcs->flush_locked(vdev, &fence);
out_unlock:
simple_mtx_unlock(&vdev->eb_lock);
if (ret)
return ret;
if (sync) {
MESA_TRACE_SCOPE("vdrm_execbuf sync");
vdev->funcs->wait_fence(vdev, fence);
vdrm_host_sync(vdev, req);
}
return 0;
}
int
vdrm_flush(struct vdrm_device *vdev)
{
int ret = 0;
MESA_TRACE_FUNC();
simple_mtx_lock(&vdev->eb_lock);
ret = vdev->funcs->flush_locked(vdev, NULL);
simple_mtx_unlock(&vdev->eb_lock);
return ret;
}
/**
* Helper for fence/seqno comparisions which deals properly with rollover.
* Returns true if fence 'a' is before fence 'b'
*/
static bool
fence_before(uint32_t a, uint32_t b)
{
return (int32_t)(a - b) < 0;
}
/**
* Wait until host has processed the specified request.
*/
void
vdrm_host_sync(struct vdrm_device *vdev, const struct vdrm_ccmd_req *req)
{
while (fence_before(vdev->shmem->seqno, req->seqno))
sched_yield();
}