Module: Mesa
Branch: main
Commit: 729ce08815a8a18d03c0e3827f5e185c6050fcd4
URL:    
http://cgit.freedesktop.org/mesa/mesa/commit/?id=729ce08815a8a18d03c0e3827f5e185c6050fcd4

Author: Tatsuyuki Ishi <ishitatsuy...@gmail.com>
Date:   Mon Nov 13 17:47:31 2023 +0900

zink: Defer freeing sparse backing buffers.

Sparse backing buffers were destroyed immediately after issuing the
unbind call, which was against the Vulkan spec which requires the
destroy call to not happen before the unbind semaphore was signaled.

To tackle this, keep a reference against buffers we are unbinding within
the batch. This will keep the backing buffer long enough to not cause
use-after-free. As described in comments, we don't need to reference
every backing page used in the batch, as the resource usually keeps
references to them until they are unbound, which is now correctly
handled.

Reviewed-by: Dave Airlie <airl...@redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26171>

---

 src/gallium/drivers/zink/zink_batch.c   | 17 ++++++++++++++++-
 src/gallium/drivers/zink/zink_bo.c      | 17 ++++++++++++++---
 src/gallium/drivers/zink/zink_bo.h      |  2 +-
 src/gallium/drivers/zink/zink_context.c |  3 +--
 src/gallium/drivers/zink/zink_types.h   |  2 ++
 5 files changed, 34 insertions(+), 7 deletions(-)

diff --git a/src/gallium/drivers/zink/zink_batch.c 
b/src/gallium/drivers/zink/zink_batch.c
index 870fb1825ae..823278ef988 100644
--- a/src/gallium/drivers/zink/zink_batch.c
+++ b/src/gallium/drivers/zink/zink_batch.c
@@ -145,6 +145,11 @@ zink_reset_batch_state(struct zink_context *ctx, struct 
zink_batch_state *bs)
 
    zink_batch_descriptor_reset(screen, bs);
 
+   util_dynarray_foreach(&bs->freed_sparse_backing_bos, struct zink_bo, bo) {
+      zink_bo_unref(screen, bo);
+   }
+   util_dynarray_clear(&bs->freed_sparse_backing_bos);
+
    /* programs are refcounted and batch-tracked */
    set_foreach_remove(&bs->programs, entry) {
       struct zink_program *pg = (struct zink_program*)entry->key;
@@ -301,6 +306,7 @@ zink_batch_state_destroy(struct zink_screen *screen, struct 
zink_batch_state *bs
    free(bs->real_objs.objs);
    free(bs->slab_objs.objs);
    free(bs->sparse_objs.objs);
+   util_dynarray_fini(&bs->freed_sparse_backing_bos);
    util_dynarray_fini(&bs->dead_querypools);
    util_dynarray_fini(&bs->dgc.pipelines);
    util_dynarray_fini(&bs->dgc.layouts);
@@ -399,6 +405,7 @@ create_batch_state(struct zink_context *ctx)
    util_dynarray_init(&bs->fd_wait_semaphore_stages, NULL);
    util_dynarray_init(&bs->zombie_samplers, NULL);
    util_dynarray_init(&bs->dead_framebuffers, NULL);
+   util_dynarray_init(&bs->freed_sparse_backing_bos, NULL);
    util_dynarray_init(&bs->unref_resources, NULL);
    util_dynarray_init(&bs->acquires, NULL);
    util_dynarray_init(&bs->acquire_flags, NULL);
@@ -1022,7 +1029,15 @@ zink_batch_reference_resource_move(struct zink_batch 
*batch, struct zink_resourc
    if (!(res->base.b.flags & PIPE_RESOURCE_FLAG_SPARSE)) {
       bs->resource_size += res->obj->size;
    } else {
-      // TODO: check backing pages
+      /* Sparse backing pages are not directly referenced by the batch as
+       * there can be a lot of them.
+       * Instead, they are kept referenced in one of two ways:
+       * - While they are committed, they are directly referenced from the
+       *   resource's state.
+       * - Upon de-commit, they are added to the freed_sparse_backing_bos
+       *   list, which will defer destroying the resource until the batch
+       *   performing unbind finishes.
+       */
    }
    check_oom_flush(batch->state->ctx, batch);
    batch->has_work = true;
diff --git a/src/gallium/drivers/zink/zink_bo.c 
b/src/gallium/drivers/zink/zink_bo.c
index 01949742732..c7a3833eb3a 100644
--- a/src/gallium/drivers/zink/zink_bo.c
+++ b/src/gallium/drivers/zink/zink_bo.c
@@ -740,6 +740,14 @@ zink_bo_unmap(struct zink_screen *screen, struct zink_bo 
*bo)
    }
 }
 
+/* see comment in zink_batch_reference_resource_move for how references on 
sparse backing buffers are organized */
+static void
+track_freed_sparse_bo(struct zink_context *ctx, struct zink_sparse_backing 
*backing)
+{
+   pipe_reference(NULL, &backing->bo->base.reference);
+   util_dynarray_append(&ctx->batch.state->freed_sparse_backing_bos, struct 
zink_bo*, backing->bo);
+}
+
 static VkSemaphore
 buffer_commit_single(struct zink_screen *screen, struct zink_resource *res, 
struct zink_bo *bo, uint32_t bo_offset, uint32_t offset, uint32_t size, bool 
commit, VkSemaphore wait)
 {
@@ -776,9 +784,10 @@ buffer_commit_single(struct zink_screen *screen, struct 
zink_resource *res, stru
 }
 
 static bool
-buffer_bo_commit(struct zink_screen *screen, struct zink_resource *res, 
uint32_t offset, uint32_t size, bool commit, VkSemaphore *sem)
+buffer_bo_commit(struct zink_context *ctx, struct zink_resource *res, uint32_t 
offset, uint32_t size, bool commit, VkSemaphore *sem)
 {
    bool ok = true;
+   struct zink_screen *screen = zink_screen(ctx->base.screen);
    struct zink_bo *bo = res->obj->bo;
    assert(offset % ZINK_SPARSE_BUFFER_PAGE_SIZE == 0);
    assert(offset <= bo->base.size);
@@ -877,6 +886,7 @@ buffer_bo_commit(struct zink_screen *screen, struct 
zink_resource *res, uint32_t
             span_pages++;
          }
 
+         track_freed_sparse_bo(ctx, backing);
          if (!sparse_backing_free(screen, bo, backing, backing_start, 
span_pages)) {
             /* Couldn't allocate tracking data structures, so we have to leak 
*/
             fprintf(stderr, "zink: leaking sparse backing memory\n");
@@ -947,9 +957,10 @@ texture_commit_miptail(struct zink_screen *screen, struct 
zink_resource *res, st
 }
 
 bool
-zink_bo_commit(struct zink_screen *screen, struct zink_resource *res, unsigned 
level, struct pipe_box *box, bool commit, VkSemaphore *sem)
+zink_bo_commit(struct zink_context *ctx, struct zink_resource *res, unsigned 
level, struct pipe_box *box, bool commit, VkSemaphore *sem)
 {
    bool ok = true;
+   struct zink_screen *screen = zink_screen(ctx->base.screen);
    struct zink_bo *bo = res->obj->bo;
    VkSemaphore cur_sem = VK_NULL_HANDLE;
 
@@ -959,7 +970,7 @@ zink_bo_commit(struct zink_screen *screen, struct 
zink_resource *res, unsigned l
    simple_mtx_lock(&screen->queue_lock);
    simple_mtx_lock(&bo->lock);
    if (res->base.b.target == PIPE_BUFFER) {
-      ok = buffer_bo_commit(screen, res, box->x, box->width, commit, &cur_sem);
+      ok = buffer_bo_commit(ctx, res, box->x, box->width, commit, &cur_sem);
       goto out;
    }
 
diff --git a/src/gallium/drivers/zink/zink_bo.h 
b/src/gallium/drivers/zink/zink_bo.h
index d017107f23a..dc457905f00 100644
--- a/src/gallium/drivers/zink/zink_bo.h
+++ b/src/gallium/drivers/zink/zink_bo.h
@@ -140,7 +140,7 @@ void
 zink_bo_unmap(struct zink_screen *screen, struct zink_bo *bo);
 
 bool
-zink_bo_commit(struct zink_screen *screen, struct zink_resource *res, unsigned 
level, struct pipe_box *box, bool commit, VkSemaphore *sem);
+zink_bo_commit(struct zink_context *ctx, struct zink_resource *res, unsigned 
level, struct pipe_box *box, bool commit, VkSemaphore *sem);
 
 static ALWAYS_INLINE bool
 zink_bo_has_unflushed_usage(const struct zink_bo *bo)
diff --git a/src/gallium/drivers/zink/zink_context.c 
b/src/gallium/drivers/zink/zink_context.c
index 45e1ca49f8f..fe464e2e334 100644
--- a/src/gallium/drivers/zink/zink_context.c
+++ b/src/gallium/drivers/zink/zink_context.c
@@ -4774,14 +4774,13 @@ zink_resource_commit(struct pipe_context *pctx, struct 
pipe_resource *pres, unsi
 {
    struct zink_context *ctx = zink_context(pctx);
    struct zink_resource *res = zink_resource(pres);
-   struct zink_screen *screen = zink_screen(pctx->screen);
 
    /* if any current usage exists, flush the queue */
    if (zink_resource_has_unflushed_usage(res))
       zink_flush_queue(ctx);
 
    VkSemaphore sem = VK_NULL_HANDLE;
-   bool ret = zink_bo_commit(screen, res, level, box, commit, &sem);
+   bool ret = zink_bo_commit(ctx, res, level, box, commit, &sem);
    if (ret) {
       if (sem)
          zink_batch_add_wait_semaphore(&ctx->batch, sem);
diff --git a/src/gallium/drivers/zink/zink_types.h 
b/src/gallium/drivers/zink/zink_types.h
index aa104196a06..219561bfb8e 100644
--- a/src/gallium/drivers/zink/zink_types.h
+++ b/src/gallium/drivers/zink/zink_types.h
@@ -652,6 +652,8 @@ struct zink_batch_state {
    struct set active_queries; /* zink_query objects which were active at some 
point in this batch */
    struct util_dynarray dead_querypools;
 
+   struct util_dynarray freed_sparse_backing_bos;
+
    struct zink_batch_descriptor_data dd;
 
    VkDeviceSize resource_size;

Reply via email to