]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
drm/nouveau: remove fence wait code from deferred client work handler
authorBen Skeggs <bskeggs@redhat.com>
Tue, 8 May 2018 10:39:47 +0000 (20:39 +1000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 3 Aug 2018 05:47:48 +0000 (07:47 +0200)
[ Upstream commit 11e451e74050d9e9030581ce40337838acfcea5b ]

Fences attached to deferred client work items now originate from channels
belonging to the client, meaning we can be certain they've been signalled
before we destroy a client.

This closes a race that could happen if the dma_fence_wait_timeout() call
didn't succeed.  When the fence was later signalled, a use-after-free was
possible.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/gpu/drm/nouveau/nouveau_drm.c

index 591d9c29ede76a7befdf89c88c450f7236f4876a..f8e67ab5c598637c89b9ed5dc27fc52dfb9daa45 100644 (file)
@@ -116,24 +116,22 @@ nouveau_name(struct drm_device *dev)
 }
 
 static inline bool
-nouveau_cli_work_ready(struct dma_fence *fence, bool wait)
+nouveau_cli_work_ready(struct dma_fence *fence)
 {
-       if (!dma_fence_is_signaled(fence)) {
-               if (!wait)
-                       return false;
-               WARN_ON(dma_fence_wait_timeout(fence, false, 2 * HZ) <= 0);
-       }
+       if (!dma_fence_is_signaled(fence))
+               return false;
        dma_fence_put(fence);
        return true;
 }
 
 static void
-nouveau_cli_work_flush(struct nouveau_cli *cli, bool wait)
+nouveau_cli_work(struct work_struct *w)
 {
+       struct nouveau_cli *cli = container_of(w, typeof(*cli), work);
        struct nouveau_cli_work *work, *wtmp;
        mutex_lock(&cli->lock);
        list_for_each_entry_safe(work, wtmp, &cli->worker, head) {
-               if (!work->fence || nouveau_cli_work_ready(work->fence, wait)) {
+               if (!work->fence || nouveau_cli_work_ready(work->fence)) {
                        list_del(&work->head);
                        work->func(work);
                }
@@ -161,17 +159,17 @@ nouveau_cli_work_queue(struct nouveau_cli *cli, struct dma_fence *fence,
        mutex_unlock(&cli->lock);
 }
 
-static void
-nouveau_cli_work(struct work_struct *w)
-{
-       struct nouveau_cli *cli = container_of(w, typeof(*cli), work);
-       nouveau_cli_work_flush(cli, false);
-}
-
 static void
 nouveau_cli_fini(struct nouveau_cli *cli)
 {
-       nouveau_cli_work_flush(cli, true);
+       /* All our channels are dead now, which means all the fences they
+        * own are signalled, and all callback functions have been called.
+        *
+        * So, after flushing the workqueue, there should be nothing left.
+        */
+       flush_work(&cli->work);
+       WARN_ON(!list_empty(&cli->worker));
+
        usif_client_fini(cli);
        nouveau_vmm_fini(&cli->vmm);
        nvif_mmu_fini(&cli->mmu);