zink: handle broken resource mapping deadlocks

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>
This commit is contained in:
Mike Blumenkrantz 2021-06-30 14:35:02 -04:00 committed by Marge Bot
parent 5e05d98848
commit 8dd314d203
5 changed files with 45 additions and 5 deletions

View 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);
}

View 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

View file

@ -182,6 +182,15 @@ zink_bo_usage_wait(struct zink_context *ctx, struct zink_bo *bo, enum zink_resou
zink_batch_usage_wait(ctx, bo->writes);
}
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)
{

View 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;

View file

@ -131,6 +131,12 @@ zink_resource_usage_check_completion(struct zink_screen *screen, struct zink_res
return zink_bo_usage_check_completion(screen, res->obj->bo, access);
}
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)
{