nouveau: synchronize "scratch runout" destruction with the command stream
authorMarcin Ślusarz <marcin.slusarz@gmail.com>
Tue, 31 Mar 2015 20:04:31 +0000 (22:04 +0200)
committerMarcin Ślusarz <marcin.slusarz@gmail.com>
Tue, 31 Mar 2015 20:04:31 +0000 (22:04 +0200)
When nvc0_push_vbo calls nouveau_scratch_done it does not mean
scratch buffers can be freed immediately. It means "when hardware
advances to this place in the command stream the scratch buffers
can be freed".

To fix it, just postpone scratch runout destruction after current
fence is signalled.

The bug existed for a very long time. Nobody noticed, because
"scratch runout" code path is rarely executed.

Fixes hang at the very beginning of first mission in "Serious Sam 3"
on nve7/gk107. It manifested as:

nouveau E[   PFIFO][0000:01:00.0] read fault at 0x000a9e0000 [PTE] from GR/GPC0/PE_2 on channel 0x007f853000 [Sam3[17056]]

Cc: "10.4 10.5" <mesa-stable@lists.freedesktop.org>
Reviewed-by: Ilia Mirkin <imirkin@alum.mit.edu>
src/gallium/drivers/nouveau/nouveau_buffer.c
src/gallium/drivers/nouveau/nouveau_context.h

index 49ff100c4ece47b5006cf4c0cd6872242dd74bb1..32fa65c8a51c577a8fbb6a48bb21ebe271eda459 100644 (file)
@@ -846,17 +846,28 @@ nouveau_scratch_bo_alloc(struct nouveau_context *nv, struct nouveau_bo **pbo,
                          4096, size, NULL, pbo);
 }
 
+static void
+nouveau_scratch_unref_bos(void *d)
+{
+   struct runout *b = d;
+   int i;
+
+   for (i = 0; i < b->nr; ++i)
+      nouveau_bo_ref(NULL, &b->bo[i]);
+
+   FREE(b);
+}
+
 void
 nouveau_scratch_runout_release(struct nouveau_context *nv)
 {
-   if (!nv->scratch.nr_runout)
+   if (!nv->scratch.runout)
+      return;
+
+   if (!nouveau_fence_work(nv->screen->fence.current, nouveau_scratch_unref_bos,
+         nv->scratch.runout))
       return;
-   do {
-      --nv->scratch.nr_runout;
-      nouveau_bo_ref(NULL, &nv->scratch.runout[nv->scratch.nr_runout]);
-   } while (nv->scratch.nr_runout);
 
-   FREE(nv->scratch.runout);
    nv->scratch.end = 0;
    nv->scratch.runout = NULL;
 }
@@ -868,21 +879,26 @@ static INLINE boolean
 nouveau_scratch_runout(struct nouveau_context *nv, unsigned size)
 {
    int ret;
-   const unsigned n = nv->scratch.nr_runout++;
+   unsigned n;
 
-   nv->scratch.runout = REALLOC(nv->scratch.runout,
-                                (n + 0) * sizeof(*nv->scratch.runout),
-                                (n + 1) * sizeof(*nv->scratch.runout));
-   nv->scratch.runout[n] = NULL;
-
-   ret = nouveau_scratch_bo_alloc(nv, &nv->scratch.runout[n], size);
+   if (nv->scratch.runout)
+      n = nv->scratch.runout->nr;
+   else
+      n = 0;
+   nv->scratch.runout = REALLOC(nv->scratch.runout, n == 0 ? 0 :
+                                (sizeof(*nv->scratch.runout) + (n + 0) * sizeof(void *)),
+                                 sizeof(*nv->scratch.runout) + (n + 1) * sizeof(void *));
+   nv->scratch.runout->nr = n + 1;
+   nv->scratch.runout->bo[n] = NULL;
+
+   ret = nouveau_scratch_bo_alloc(nv, &nv->scratch.runout->bo[n], size);
    if (!ret) {
-      ret = nouveau_bo_map(nv->scratch.runout[n], 0, NULL);
+      ret = nouveau_bo_map(nv->scratch.runout->bo[n], 0, NULL);
       if (ret)
-         nouveau_bo_ref(NULL, &nv->scratch.runout[--nv->scratch.nr_runout]);
+         nouveau_bo_ref(NULL, &nv->scratch.runout->bo[--nv->scratch.runout->nr]);
    }
    if (!ret) {
-      nv->scratch.current = nv->scratch.runout[n];
+      nv->scratch.current = nv->scratch.runout->bo[n];
       nv->scratch.offset = 0;
       nv->scratch.end = size;
       nv->scratch.map = nv->scratch.current->map;
index 14608d33c52c43ff31af97c3a541df4ded20f79f..c2ba0159afecd21c18d9a68ebd703e26bf025b54 100644 (file)
@@ -40,8 +40,10 @@ struct nouveau_context {
       unsigned end;
       struct nouveau_bo *bo[NOUVEAU_MAX_SCRATCH_BUFS];
       struct nouveau_bo *current;
-      struct nouveau_bo **runout;
-      unsigned nr_runout;
+      struct runout {
+         unsigned nr;
+         struct nouveau_bo *bo[0];
+      } *runout;
       unsigned bo_size;
    } scratch;
 
@@ -71,7 +73,7 @@ static INLINE void
 nouveau_scratch_done(struct nouveau_context *nv)
 {
    nv->scratch.wrap = nv->scratch.id;
-   if (unlikely(nv->scratch.nr_runout))
+   if (unlikely(nv->scratch.runout))
       nouveau_scratch_runout_release(nv);
 }