nouveau: fix fence hang
authorMarcin Slusarz <marcin.slusarz@gmail.com>
Sat, 8 Oct 2011 21:05:25 +0000 (23:05 +0200)
committerMarcin Slusarz <marcin.slusarz@gmail.com>
Sun, 9 Oct 2011 12:49:30 +0000 (14:49 +0200)
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.

src/gallium/drivers/nouveau/nouveau_fence.c
src/gallium/drivers/nouveau/nouveau_fence.h
src/gallium/drivers/nouveau/nouveau_screen.h
src/gallium/drivers/nv50/nv50_screen.c
src/gallium/drivers/nvc0/nvc0_screen.c

index 26e4775b12df829485093ec6d60334712b31a7e8..a2fcafef4a7bea8fc2eeeb97cb01f85c3a8164c2 100644 (file)
@@ -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);
index 680c75e99f962ec17862da79c44fba46ebc050f2..3984a9a942d213c9a5986f151750e1072d2f5c46 100644 (file)
@@ -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;
index cf291c6c59505cbf35202249cc437089c507d27a..808500f3002ff838d94c8bbcdf831afa0663c404 100644 (file)
@@ -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;
 
index c36f3cd4a0455a7545c9ee3ab992f35ff862f68a..7ff11ea4b0bc4d2e76695b02bd93830b90646e9b 100644 (file)
@@ -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 |
index cc6e6830af283c6f5c8daa3349c072cace013bce..292cbddcc0c0070ffce4b450db446ccb837ac83e 100644 (file)
@@ -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));
 }