drm/nouveau: Synchronize with the user channel before GPU object destruction.
authorFrancisco Jerez <currojerez@riseup.net>
Thu, 18 Nov 2010 22:57:46 +0000 (23:57 +0100)
committerFrancisco Jerez <currojerez@riseup.net>
Wed, 8 Dec 2010 02:00:23 +0000 (03:00 +0100)
There have been reports of PFIFO cache errors during context take down
(fdo bug 31637). They are caused by some GPU objects being taken out
while the channel is still potentially processing commands. Make sure
that all the previous rendering has landed before releasing a GPU
object.

Reported-by: Grzesiek Sójka <pld@pfu.pl>
Reported-by: Patrice Mandin <patmandin@gmail.com>
Signed-off-by: Francisco Jerez <currojerez@riseup.net>
Acked-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_channel.c
drivers/gpu/drm/nouveau/nouveau_drv.c
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_object.c

index 0f33132fba3b7e9418cf66757da36a2cd80335f5..3e49babd62ace02da6205caeedeb97f2646a9e3d 100644 (file)
@@ -284,7 +284,6 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan)
        struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
        struct nouveau_crypt_engine *pcrypt = &dev_priv->engine.crypt;
        unsigned long flags;
-       int ret;
 
        /* decrement the refcount, and we're done if there's still refs */
        if (likely(!atomic_dec_and_test(&chan->users))) {
@@ -297,19 +296,7 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan)
        nouveau_debugfs_channel_fini(chan);
 
        /* give it chance to idle */
-       nouveau_fence_update(chan);
-       if (chan->fence.sequence != chan->fence.sequence_ack) {
-               struct nouveau_fence *fence = NULL;
-
-               ret = nouveau_fence_new(chan, &fence, true);
-               if (ret == 0) {
-                       ret = nouveau_fence_wait(fence, false, false);
-                       nouveau_fence_unref(&fence);
-               }
-
-               if (ret)
-                       NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id);
-       }
+       nouveau_channel_idle(chan);
 
        /* ensure all outstanding fences are signaled.  they should be if the
         * above attempts at idling were OK, but if we failed this'll tell TTM
@@ -388,6 +375,27 @@ nouveau_channel_ref(struct nouveau_channel *chan,
        *pchan = chan;
 }
 
+void
+nouveau_channel_idle(struct nouveau_channel *chan)
+{
+       struct drm_device *dev = chan->dev;
+       struct nouveau_fence *fence = NULL;
+       int ret;
+
+       nouveau_fence_update(chan);
+
+       if (chan->fence.sequence != chan->fence.sequence_ack) {
+               ret = nouveau_fence_new(chan, &fence, true);
+               if (!ret) {
+                       ret = nouveau_fence_wait(fence, false, false);
+                       nouveau_fence_unref(&fence);
+               }
+
+               if (ret)
+                       NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id);
+       }
+}
+
 /* cleans up all the fifos from file_priv */
 void
 nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv)
index 7ff5b4369f03183c5882c9b1e01f155479bc8a0b..a48c7da133d2cc1fb6d921ff7e8869ed94ff5634 100644 (file)
@@ -197,22 +197,10 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
 
        NV_INFO(dev, "Idling channels...\n");
        for (i = 0; i < pfifo->channels; i++) {
-               struct nouveau_fence *fence = NULL;
-
                chan = dev_priv->channels.ptr[i];
-               if (!chan || !chan->pushbuf_bo)
-                       continue;
-
-               ret = nouveau_fence_new(chan, &fence, true);
-               if (ret == 0) {
-                       ret = nouveau_fence_wait(fence, false, false);
-                       nouveau_fence_unref(&fence);
-               }
 
-               if (ret) {
-                       NV_ERROR(dev, "Failed to idle channel %d for suspend\n",
-                                chan->id);
-               }
+               if (chan && chan->pushbuf_bo)
+                       nouveau_channel_idle(chan);
        }
 
        pgraph->fifo_access(dev, false);
index a52b1da32031a795f24a9e7377dfd81f1fc495ca..d001453e857b30755b11061540d8e3cfc48648ba 100644 (file)
@@ -847,6 +847,7 @@ extern void nouveau_channel_put_unlocked(struct nouveau_channel **);
 extern void nouveau_channel_put(struct nouveau_channel **);
 extern void nouveau_channel_ref(struct nouveau_channel *chan,
                                struct nouveau_channel **pchan);
+extern void nouveau_channel_idle(struct nouveau_channel *chan);
 
 /* nouveau_object.c */
 #define NVOBJ_CLASS(d,c,e) do {                                                \
index 2fb7e9d47500d708363e0b43deae0cde19141ab3..24540862a23f02feb8370fb9234ec30410dc18d5 100644 (file)
@@ -1017,6 +1017,9 @@ int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data,
        if (IS_ERR(chan))
                return PTR_ERR(chan);
 
+       /* Synchronize with the user channel */
+       nouveau_channel_idle(chan);
+
        ret = nouveau_ramht_remove(chan, objfree->handle);
        nouveau_channel_put(&chan);
        return ret;