From 01ef3152c98956d7b75ad5148abeda078618941f Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Sat, 21 Sep 2024 01:26:03 +0900 Subject: [PATCH] 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 Part-of: --- src/asahi/lib/agx_device.c | 2 +- src/asahi/lib/agx_device.h | 13 ++++++++++++- src/asahi/lib/agx_device_virtio.c | 12 ++++++++++-- src/asahi/lib/asahi_proto.h | 9 +++++++++ src/asahi/vulkan/hk_queue.c | 8 +++++++- src/gallium/drivers/asahi/agx_batch.c | 25 ++++++++++++++++++++++++- 6 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/asahi/lib/agx_device.c b/src/asahi/lib/agx_device.c index a6283302f4d..b2703aac2ae 100644 --- a/src/asahi/lib/agx_device.c +++ b/src/asahi/lib/agx_device.c @@ -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); } diff --git a/src/asahi/lib/agx_device.h b/src/asahi/lib/agx_device.h index 37232147d22..bc5f9ff7d11 100644 --- a/src/asahi/lib/agx_device.h +++ b/src/asahi/lib/agx_device.h @@ -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 { diff --git a/src/asahi/lib/agx_device_virtio.c b/src/asahi/lib/agx_device_virtio.c index 7ab3fb04cd7..83ce8d7fedf 100644 --- a/src/asahi/lib/agx_device_virtio.c +++ b/src/asahi/lib/agx_device_virtio.c @@ -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; diff --git a/src/asahi/lib/asahi_proto.h b/src/asahi/lib/asahi_proto.h index 8820b46a5e7..577f701395e 100644 --- a/src/asahi/lib/asahi_proto.h +++ b/src/asahi/lib/asahi_proto.h @@ -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[]; }; diff --git a/src/asahi/vulkan/hk_queue.c b/src/asahi/vulkan/hk_queue.c index 7eb20b6d277..4c1efc4eedf 100644 --- a/src/asahi/vulkan/hk_queue.c +++ b/src/asahi/vulkan/hk_queue.c @@ -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) { diff --git a/src/gallium/drivers/asahi/agx_batch.c b/src/gallium/drivers/asahi/agx_batch.c index 455b0f1c16a..a5999532a12 100644 --- a/src/gallium/drivers/asahi/agx_batch.c +++ b/src/gallium/drivers/asahi/agx_batch.c @@ -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;