mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-01-22 19:20:22 +01:00
nouveau: fix fence hang
If there is not enough space in pushbuffer for fence emission (nouveau_fence_emit -> nv50_screen_fence_emit -> MARK_RING), the pushbuffer is flushed, which through flush_notify -> nv50_default_flush_notify -> nouveau_fence_update marks currently emitting fence as flushed. But actual emission is done after this mark. So later when there is a need to wait on this fence and pushbuffer was not flushed in between, fence wait will never finish causing application to hang. To fix this, introduce new fence state between AVAILABLE and EMITTED, set it before emission and handle it everywhere. Additionally obtain fence sequence numbers after possible flush in MARK_RING, because we want to emit fences in correct order. Reviewed-by: Christoph Bumiller <e0425955@student.tuwien.ac.at> Note: This is a candidate for the 7.11 branch.
This commit is contained in:
parent
0110aa09e5
commit
9849f366cb
5 changed files with 28 additions and 14 deletions
|
|
@ -86,12 +86,10 @@ nouveau_fence_emit(struct nouveau_fence *fence)
|
|||
{
|
||||
struct nouveau_screen *screen = fence->screen;
|
||||
|
||||
fence->sequence = ++screen->fence.sequence;
|
||||
|
||||
assert(fence->state == NOUVEAU_FENCE_STATE_AVAILABLE);
|
||||
|
||||
/* set this now, so that if fence.emit triggers a flush we don't recurse */
|
||||
fence->state = NOUVEAU_FENCE_STATE_EMITTED;
|
||||
fence->state = NOUVEAU_FENCE_STATE_EMITTING;
|
||||
|
||||
++fence->ref;
|
||||
|
||||
|
|
@ -102,7 +100,10 @@ nouveau_fence_emit(struct nouveau_fence *fence)
|
|||
|
||||
screen->fence.tail = fence;
|
||||
|
||||
screen->fence.emit(&screen->base, fence->sequence);
|
||||
screen->fence.emit(&screen->base, &fence->sequence);
|
||||
|
||||
assert(fence->state == NOUVEAU_FENCE_STATE_EMITTING);
|
||||
fence->state = NOUVEAU_FENCE_STATE_EMITTED;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -162,7 +163,8 @@ nouveau_fence_update(struct nouveau_screen *screen, boolean flushed)
|
|||
|
||||
if (flushed) {
|
||||
for (fence = next; fence; fence = fence->next)
|
||||
fence->state = NOUVEAU_FENCE_STATE_FLUSHED;
|
||||
if (fence->state == NOUVEAU_FENCE_STATE_EMITTED)
|
||||
fence->state = NOUVEAU_FENCE_STATE_FLUSHED;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -185,6 +187,9 @@ nouveau_fence_wait(struct nouveau_fence *fence)
|
|||
struct nouveau_screen *screen = fence->screen;
|
||||
uint32_t spins = 0;
|
||||
|
||||
/* wtf, someone is waiting on a fence in flush_notify handler? */
|
||||
assert(fence->state != NOUVEAU_FENCE_STATE_EMITTING);
|
||||
|
||||
if (fence->state < NOUVEAU_FENCE_STATE_EMITTED) {
|
||||
nouveau_fence_emit(fence);
|
||||
|
||||
|
|
@ -216,7 +221,7 @@ nouveau_fence_wait(struct nouveau_fence *fence)
|
|||
void
|
||||
nouveau_fence_next(struct nouveau_screen *screen)
|
||||
{
|
||||
if (screen->fence.current->state < NOUVEAU_FENCE_STATE_EMITTED)
|
||||
if (screen->fence.current->state < NOUVEAU_FENCE_STATE_EMITTING)
|
||||
nouveau_fence_emit(screen->fence.current);
|
||||
|
||||
nouveau_fence_ref(NULL, &screen->fence.current);
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@
|
|||
#include "util/u_double_list.h"
|
||||
|
||||
#define NOUVEAU_FENCE_STATE_AVAILABLE 0
|
||||
#define NOUVEAU_FENCE_STATE_EMITTED 1
|
||||
#define NOUVEAU_FENCE_STATE_FLUSHED 2
|
||||
#define NOUVEAU_FENCE_STATE_SIGNALLED 3
|
||||
#define NOUVEAU_FENCE_STATE_EMITTING 1
|
||||
#define NOUVEAU_FENCE_STATE_EMITTED 2
|
||||
#define NOUVEAU_FENCE_STATE_FLUSHED 3
|
||||
#define NOUVEAU_FENCE_STATE_SIGNALLED 4
|
||||
|
||||
struct nouveau_fence_work {
|
||||
struct list_head list;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ struct nouveau_screen {
|
|||
struct nouveau_fence *current;
|
||||
u32 sequence;
|
||||
u32 sequence_ack;
|
||||
void (*emit)(struct pipe_screen *, u32 sequence);
|
||||
void (*emit)(struct pipe_screen *, u32 *sequence);
|
||||
u32 (*update)(struct pipe_screen *);
|
||||
} fence;
|
||||
|
||||
|
|
|
|||
|
|
@ -261,16 +261,20 @@ nv50_screen_destroy(struct pipe_screen *pscreen)
|
|||
}
|
||||
|
||||
static void
|
||||
nv50_screen_fence_emit(struct pipe_screen *pscreen, u32 sequence)
|
||||
nv50_screen_fence_emit(struct pipe_screen *pscreen, u32 *sequence)
|
||||
{
|
||||
struct nv50_screen *screen = nv50_screen(pscreen);
|
||||
struct nouveau_channel *chan = screen->base.channel;
|
||||
|
||||
MARK_RING (chan, 5, 2);
|
||||
|
||||
/* we need to do it after possible flush in MARK_RING */
|
||||
*sequence = ++screen->base.fence.sequence;
|
||||
|
||||
BEGIN_RING(chan, RING_3D(QUERY_ADDRESS_HIGH), 4);
|
||||
OUT_RELOCh(chan, screen->fence.bo, 0, NOUVEAU_BO_WR);
|
||||
OUT_RELOCl(chan, screen->fence.bo, 0, NOUVEAU_BO_WR);
|
||||
OUT_RING (chan, sequence);
|
||||
OUT_RING (chan, *sequence);
|
||||
OUT_RING (chan, NV50_3D_QUERY_GET_MODE_WRITE_UNK0 |
|
||||
NV50_3D_QUERY_GET_UNK4 |
|
||||
NV50_3D_QUERY_GET_UNIT_CROP |
|
||||
|
|
|
|||
|
|
@ -334,16 +334,20 @@ nvc0_magic_3d_init(struct nouveau_channel *chan)
|
|||
}
|
||||
|
||||
static void
|
||||
nvc0_screen_fence_emit(struct pipe_screen *pscreen, u32 sequence)
|
||||
nvc0_screen_fence_emit(struct pipe_screen *pscreen, u32 *sequence)
|
||||
{
|
||||
struct nvc0_screen *screen = nvc0_screen(pscreen);
|
||||
struct nouveau_channel *chan = screen->base.channel;
|
||||
|
||||
MARK_RING (chan, 5, 2);
|
||||
|
||||
/* we need to do it after possible flush in MARK_RING */
|
||||
*sequence = ++screen->base.fence.sequence;
|
||||
|
||||
BEGIN_RING(chan, RING_3D(QUERY_ADDRESS_HIGH), 4);
|
||||
OUT_RELOCh(chan, screen->fence.bo, 0, NOUVEAU_BO_WR);
|
||||
OUT_RELOCl(chan, screen->fence.bo, 0, NOUVEAU_BO_WR);
|
||||
OUT_RING (chan, sequence);
|
||||
OUT_RING (chan, *sequence);
|
||||
OUT_RING (chan, NVC0_3D_QUERY_GET_FENCE | NVC0_3D_QUERY_GET_SHORT |
|
||||
(0xf << NVC0_3D_QUERY_GET_UNIT__SHIFT));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue