[Nouveau] [PATCH] drm/nouveau: fix ref leak in nouveau_gem_pushbuf_validate()

Pekka Paalanen pq at iki.fi
Mon Sep 7 13:09:25 PDT 2009


If ttm_bo_reserve() in nouveau_gem_pushbuf_validate() failed, the GEM
object reference is leaked, since the object is not yet in the list for
cleanup.

Create a new function nouveau_gem_pushbuf_lookup_and_reserve() that does
the GEM object lookup and ttm_bo_reserve() together, or fails and undos
them both, eliminating GEM object reference leaks.

I believe this bug lead to a permanent validation failure, which was
worked around in the earlier commit "drm/nouveau: bail out if validation
fails repeatedly". I also believe this bug later triggered a BUG_ON() in
TTM cleanup during module unload, making nouveau.ko impossible to
unload. The relevant bug report:
https://bugs.freedesktop.org/show_bug.cgi?id=23741

This patch also includes Ben's cleanup fix in
nouveau_gem_pushbuf_validate(), where the current object ref and
reservervation would have not been cancelled, if cpu_writers test
failed.

However, this patch does NOT fix nouveau_gem_pushbuf_validate()
completely. The scenario described in the bug report now leads to X
server being stuck in uninterruptible sleep in ttm_bo_wait_unreserved().

Cc: Ben Skeggs <bskeggs at redhat.com>
Signed-off-by: Pekka Paalanen <pq at iki.fi>
---
 drivers/gpu/drm/nouveau/nouveau_gem.c |   65 +++++++++++++++++++++------------
 1 files changed, 42 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index f0c78a8..55e7988 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -268,6 +268,40 @@ nouveau_gem_pushbuf_fence(struct list_head *list, struct nouveau_fence *fence)
 }
 
 static int
+nouveau_gem_pushbuf_lookup_and_reserve(struct nouveau_channel *chan,
+				struct drm_file *file_priv,
+				struct drm_nouveau_gem_pushbuf_bo *pbbo,
+				struct nouveau_bo **bop)
+{
+	struct drm_gem_object *gem;
+	struct nouveau_bo *nvbo;
+	int ret;
+
+	gem = drm_gem_object_lookup(chan->dev, file_priv, pbbo->handle);
+	if (!gem) {
+		NV_ERROR(chan->dev, "Unknown handle 0x%08x\n", pbbo->handle);
+		return -EINVAL;
+	}
+	nvbo = gem->driver_private;
+
+	ret = ttm_bo_reserve(&nvbo->bo, false, false, true,
+			     chan->fence.sequence);
+	switch (ret) {
+	case 0:
+		*bop = nvbo;
+		return 0;
+	case -EAGAIN:
+		ret = ttm_bo_wait_unreserved(&nvbo->bo, false);
+		if (ret == 0)
+			ret = -EAGAIN;
+		/* fall through */
+	default:
+		drm_gem_object_unreference(gem);
+	}
+	return ret;
+}
+
+static int
 nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
 			     struct drm_file *file_priv,
 			     struct drm_nouveau_gem_pushbuf_bo *pbbo,
@@ -279,7 +313,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
 	struct drm_nouveau_gem_pushbuf_bo __user *user_pbbos =
 				(void __force __user *)(uintptr_t)user_buffers;
 	struct nouveau_fence *prev_fence;
-	struct nouveau_bo *nvbo;
+	struct nouveau_bo *nvbo = NULL;
 	struct list_head *entry, *tmp;
 	int ret = -EINVAL;
 	int i;
@@ -296,28 +330,15 @@ retry:
 	}
 
 	for (i = 0, b = pbbo; i < nr_buffers; i++, b++) {
-		struct drm_gem_object *gem;
-
-		gem = drm_gem_object_lookup(dev, file_priv, b->handle);
-		if (!gem) {
-			NV_ERROR(dev, "Unknown handle 0x%08x\n", b->handle);
-			ret = -EINVAL;
-			goto out_unref;
-		}
-		nvbo = gem->driver_private;
-
-		ret = ttm_bo_reserve(&nvbo->bo, false, false, true,
-				     chan->fence.sequence);
-		if (ret) {
+		ret = nouveau_gem_pushbuf_lookup_and_reserve(chan, file_priv,
+								b, &nvbo);
+		if (ret == -EAGAIN) {
 			nouveau_gem_pushbuf_backoff(list);
-			if (ret == -EAGAIN) {
-				ret = ttm_bo_wait_unreserved(&nvbo->bo, false);
-				if (unlikely(ret))
-					goto out_unref;
-				goto retry;
-			} else
-				goto out_unref;
+			goto retry;
 		}
+		if (ret)
+			goto out_unref;
+		list_add_tail(&nvbo->entry, list);
 
 		if (unlikely(atomic_read(&nvbo->bo.cpu_writers) > 0)) {
 			nouveau_gem_pushbuf_backoff(list);
@@ -326,8 +347,6 @@ retry:
 				goto out_unref;
 			goto retry;
 		}
-
-		list_add_tail(&nvbo->entry, list);
 	}
 
 	b = pbbo;
-- 
1.6.3.3



More information about the Nouveau mailing list