agx: Fix implicit sync for virtgpu

The asahi kernel driver is a pure-explicit-sync driver and userspace is
required to handle implicit sync itself, by importing/exporting fences
in shared dma-bufs. Mesa handles this in its own native or guest
context, but dma-buf fences are not shared between the guest and the
host, so this breaks implicit sync across the VM boundary.

To make this work, explicitly pass a resource list to the host so it can
perform the implicit sync dance, like we do in agx_batch.c. This
essentially turns the virtgpu protocol into an implicit sync protocol
(like many other legacy GPU drivers), which makes sense here since we
don't particularly have the primitives to pass through and manage "host"
syncobjs that we'd need to do it at that level.

Signed-off-by: Asahi Lina <lina@asahilina.net>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/31532>
This commit is contained in:
Asahi Lina 2024-09-21 01:26:03 +09:00 committed by Marge Bot
parent 6f3c1c909b
commit 01ef3152c9
6 changed files with 63 additions and 6 deletions

View file

@ -382,7 +382,7 @@ agx_get_params(struct agx_device *dev, void *buf, size_t size)
static int
agx_submit(struct agx_device *dev, struct drm_asahi_submit *submit,
uint32_t vbo_res_id)
struct agx_submit_virt *virt)
{
return drmIoctl(dev->fd, DRM_IOCTL_ASAHI_SUBMIT, submit);
}

View file

@ -17,6 +17,11 @@
#include "layout.h"
#include "unstable_asahi_drm.h"
#include "vdrm.h"
#include "virglrenderer_hw.h"
#include "asahi_proto.h"
// TODO: this is a lie right now
static const uint64_t AGX_SUPPORTED_INCOMPAT_FEATURES =
DRM_ASAHI_FEAT_MANDATORY_ZS_COMPRESSION;
@ -61,6 +66,12 @@ struct nir_shader;
#define BARRIER_RENDER (1 << DRM_ASAHI_SUBQUEUE_RENDER)
#define BARRIER_COMPUTE (1 << DRM_ASAHI_SUBQUEUE_COMPUTE)
struct agx_submit_virt {
uint32_t vbo_res_id;
uint32_t extres_count;
struct asahi_ccmd_submit_res *extres;
};
typedef struct {
struct agx_bo *(*bo_alloc)(struct agx_device *dev, size_t size, size_t align,
enum agx_bo_flags flags);
@ -70,7 +81,7 @@ typedef struct {
void (*bo_mmap)(struct agx_device *dev, struct agx_bo *bo);
ssize_t (*get_params)(struct agx_device *dev, void *buf, size_t size);
int (*submit)(struct agx_device *dev, struct drm_asahi_submit *submit,
uint32_t vbo_res_id);
struct agx_submit_virt *virt);
} agx_device_ops_t;
struct agx_device {

View file

@ -194,7 +194,7 @@ out:
static int
agx_virtio_submit(struct agx_device *dev, struct drm_asahi_submit *submit,
uint32_t vbo_res_id)
struct agx_submit_virt *virt)
{
struct drm_asahi_command *commands =
(struct drm_asahi_command *)submit->commands;
@ -226,12 +226,17 @@ agx_virtio_submit(struct agx_device *dev, struct drm_asahi_submit *submit,
}
}
size_t extres_size =
sizeof(struct asahi_ccmd_submit_res) * virt->extres_count;
req_len += extres_size;
struct asahi_ccmd_submit_req *req =
(struct asahi_ccmd_submit_req *)calloc(1, req_len);
req->queue_id = submit->queue_id;
req->result_res_id = vbo_res_id;
req->result_res_id = virt->vbo_res_id;
req->command_count = submit->command_count;
req->extres_count = virt->extres_count;
char *ptr = (char *)&req->payload;
@ -252,6 +257,9 @@ agx_virtio_submit(struct agx_device *dev, struct drm_asahi_submit *submit,
}
}
memcpy(ptr, virt->extres, extres_size);
ptr += extres_size;
req->hdr.cmd = ASAHI_CCMD_SUBMIT;
req->hdr.len = req_len;

View file

@ -120,11 +120,20 @@ struct asahi_ccmd_gem_bind_rsp {
int32_t ret;
};
#define ASAHI_EXTRES_READ 0x01
#define ASAHI_EXTRES_WRITE 0x02
struct asahi_ccmd_submit_res {
uint32_t res_id;
uint32_t flags;
};
struct asahi_ccmd_submit_req {
struct vdrm_ccmd_req hdr;
uint32_t queue_id;
uint32_t result_res_id;
uint32_t command_count;
uint32_t extres_count;
uint8_t payload[];
};

View file

@ -299,7 +299,13 @@ union drm_asahi_cmd {
static VkResult
queue_submit_single(struct agx_device *dev, struct drm_asahi_submit *submit)
{
int ret = dev->ops.submit(dev, submit, 0);
/* Currently we don't use the result buffer or implicit sync */
struct agx_submit_virt virt = {
.vbo_res_id = 0,
.extres_count = 0,
};
int ret = dev->ops.submit(dev, submit, &virt);
/* XXX: don't trap */
if (ret) {

View file

@ -764,6 +764,10 @@ agx_batch_submit(struct agx_context *ctx, struct agx_batch *batch,
uint64_t wait_seqid = p_atomic_read(&screen->flush_wait_seqid);
struct agx_submit_virt virt = {
.vbo_res_id = ctx->result_buf->vbo_res_id,
};
/* Elide syncing against our own queue */
if (wait_seqid && wait_seqid == ctx->flush_my_seqid) {
batch_debug(batch,
@ -859,6 +863,8 @@ agx_batch_submit(struct agx_context *ctx, struct agx_batch *batch,
/* And keep track of the BO for cloning the out_sync */
shared_bos[shared_bo_count++] = bo;
if (dev->is_virtio)
virt.extres_count++;
} else {
/* Deal with BOs which are not externally shared, but which have been
* written from another context within the same screen. We also need to
@ -878,6 +884,20 @@ agx_batch_submit(struct agx_context *ctx, struct agx_batch *batch,
}
}
if (dev->is_virtio && virt.extres_count) {
struct agx_bo **p = shared_bos;
virt.extres =
malloc(virt.extres_count * sizeof(struct asahi_ccmd_submit_res));
for (unsigned i = 0; i < virt.extres_count; i++) {
while (!*p)
p++; // Skip inter-context slots which are not recorded here
virt.extres[i].res_id = (*p)->vbo_res_id;
virt.extres[i].flags = ASAHI_EXTRES_READ | ASAHI_EXTRES_WRITE;
p++;
}
}
if (dev->debug & AGX_DBG_SCRATCH) {
if (compute)
agx_scratch_debug_pre(&ctx->scratch_cs);
@ -944,7 +964,7 @@ agx_batch_submit(struct agx_context *ctx, struct agx_batch *batch,
.commands = (uint64_t)(uintptr_t)(&commands[0]),
};
int ret = dev->ops.submit(dev, &submit, ctx->result_buf->vbo_res_id);
int ret = dev->ops.submit(dev, &submit, &virt);
u_rwlock_rdunlock(&screen->destroy_lock);
@ -1054,6 +1074,9 @@ agx_batch_submit(struct agx_context *ctx, struct agx_batch *batch,
agx_batch_mark_submitted(batch);
if (virt.extres)
free(virt.extres);
/* Record the last syncobj for fence creation */
ctx->syncobj = batch->syncobj;