zink: handle broken resource mapping deadlocks
authorMike Blumenkrantz <michael.blumenkrantz@gmail.com>
Wed, 30 Jun 2021 18:35:02 +0000 (14:35 -0400)
committerMarge Bot <emma+marge@anholt.net>
Wed, 26 Oct 2022 18:29:16 +0000 (18:29 +0000)
some apps (most notably Wolfenstein: The New Order) have broken multi-context
buffer usage in which one context will attempt to write to a buffer while
another context holds unflushed usage, and the unflushed context will never
flush until the buffer write completes

it's impossible to handle this scenario correctly without deadlocking,
so add some handling to try waiting and then yolo the buffer write if
a deadlock would occur

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/19141>

src/gallium/drivers/zink/zink_batch.c
src/gallium/drivers/zink/zink_batch.h
src/gallium/drivers/zink/zink_bo.h
src/gallium/drivers/zink/zink_resource.c
src/gallium/drivers/zink/zink_resource.h

index 5df3887..28c60d8 100644 (file)
@@ -804,8 +804,8 @@ zink_batch_usage_check_completion(struct zink_context *ctx, const struct zink_ba
    return zink_check_batch_completion(ctx, u->usage);
 }
 
-void
-zink_batch_usage_wait(struct zink_context *ctx, struct zink_batch_usage *u)
+static void
+batch_usage_wait(struct zink_context *ctx, struct zink_batch_usage *u, bool trywait)
 {
    if (!zink_batch_usage_exists(u))
       return;
@@ -814,9 +814,25 @@ zink_batch_usage_wait(struct zink_context *ctx, struct zink_batch_usage *u)
          ctx->base.flush(&ctx->base, NULL, PIPE_FLUSH_HINT_FINISH);
       else { //multi-context
          mtx_lock(&u->mtx);
-         cnd_wait(&u->flush, &u->mtx);
+         if (trywait) {
+            struct timespec ts = {0, 10000};
+            cnd_timedwait(&u->flush, &u->mtx, &ts);
+         } else
+            cnd_wait(&u->flush, &u->mtx);
          mtx_unlock(&u->mtx);
       }
    }
    zink_wait_on_batch(ctx, u->usage);
 }
+
+void
+zink_batch_usage_wait(struct zink_context *ctx, struct zink_batch_usage *u)
+{
+   batch_usage_wait(ctx, u, false);
+}
+
+void
+zink_batch_usage_try_wait(struct zink_context *ctx, struct zink_batch_usage *u)
+{
+   batch_usage_wait(ctx, u, true);
+}
index f911a4f..9c9caa1 100644 (file)
@@ -121,6 +121,9 @@ zink_batch_usage_check_completion(struct zink_context *ctx, const struct zink_ba
 void
 zink_batch_usage_wait(struct zink_context *ctx, struct zink_batch_usage *u);
 
+void
+zink_batch_usage_try_wait(struct zink_context *ctx, struct zink_batch_usage *u);
+
 #ifdef __cplusplus
 }
 #endif
index 4d3a89b..b58c6c2 100644 (file)
@@ -183,6 +183,15 @@ zink_bo_usage_wait(struct zink_context *ctx, struct zink_bo *bo, enum zink_resou
 }
 
 static inline void
+zink_bo_usage_try_wait(struct zink_context *ctx, struct zink_bo *bo, enum zink_resource_access access)
+{
+   if (access & ZINK_RESOURCE_ACCESS_READ)
+      zink_batch_usage_try_wait(ctx, bo->reads);
+   if (access & ZINK_RESOURCE_ACCESS_WRITE)
+      zink_batch_usage_try_wait(ctx, bo->writes);
+}
+
+static inline void
 zink_bo_usage_set(struct zink_bo *bo, struct zink_batch_state *bs, bool write)
 {
    if (write)
index af55ab2..e3b793e 100644 (file)
@@ -1859,6 +1859,7 @@ zink_buffer_map(struct pipe_context *pctx,
               !res->obj->host_visible)) {
       assert(!(usage & (TC_TRANSFER_MAP_THREADED_UNSYNC | PIPE_MAP_THREAD_SAFE)));
       if (!res->obj->host_visible || !(usage & PIPE_MAP_ONCE)) {
+overwrite:
          trans->offset = box->x % screen->info.props.limits.minMemoryMapAlignment;
          trans->staging_res = pipe_buffer_create(&screen->base, PIPE_BIND_LINEAR, PIPE_USAGE_STAGING, box->width + trans->offset);
          if (!trans->staging_res)
@@ -1880,9 +1881,14 @@ zink_buffer_map(struct pipe_context *pctx,
    }
 
    if (!(usage & PIPE_MAP_UNSYNCHRONIZED)) {
-      if (usage & PIPE_MAP_WRITE)
+      if (usage & PIPE_MAP_WRITE) {
+         if (!(usage & PIPE_MAP_READ)) {
+            zink_resource_usage_try_wait(ctx, res, ZINK_RESOURCE_ACCESS_RW);
+            if (zink_resource_has_unflushed_usage(res))
+               goto overwrite;
+         }
          zink_resource_usage_wait(ctx, res, ZINK_RESOURCE_ACCESS_RW);
-      else
+      else
          zink_resource_usage_wait(ctx, res, ZINK_RESOURCE_ACCESS_WRITE);
       res->obj->access = 0;
       res->obj->access_stage = 0;
index 4f2eb8f..652585c 100644 (file)
@@ -132,6 +132,12 @@ zink_resource_usage_check_completion(struct zink_screen *screen, struct zink_res
 }
 
 static inline void
+zink_resource_usage_try_wait(struct zink_context *ctx, struct zink_resource *res, enum zink_resource_access access)
+{
+   zink_bo_usage_try_wait(ctx, res->obj->bo, access);
+}
+
+static inline void
 zink_resource_usage_wait(struct zink_context *ctx, struct zink_resource *res, enum zink_resource_access access)
 {
    zink_bo_usage_wait(ctx, res->obj->bo, access);