From e05d7eaeba57921abad0ef564b0875e225171de8 Mon Sep 17 00:00:00 2001
From: Ben Skeggs <bskeggs@redhat.com>
Date: Wed, 1 Sep 2010 15:24:38 +1000
Subject: [PATCH] drm/nouveau: protect gpuobj list + global instmem heap with
 spinlock

Reviewed-by: Francisco Jerez <currojerez@riseup.net>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
---
 drivers/gpu/drm/nouveau/nouveau_drv.h    |  8 +++----
 drivers/gpu/drm/nouveau/nouveau_object.c | 28 ++++++++++++++++++++----
 2 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 8ee854a4b3f5..0844f27651c0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -514,11 +514,13 @@ struct drm_nouveau_private {
 
 	void __iomem *mmio;
 
+	spinlock_t ramin_lock;
 	void __iomem *ramin;
 	u32 ramin_size;
 	u32 ramin_base;
 	bool ramin_available;
-	spinlock_t ramin_lock;
+	struct drm_mm ramin_heap;
+	struct list_head gpuobj_list;
 
 	struct nouveau_bo *vga_ram;
 
@@ -592,10 +594,6 @@ struct drm_nouveau_private {
 	struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
 	int vm_vram_pt_nr;
 
-	struct drm_mm ramin_heap;
-
-	struct list_head gpuobj_list;
-
 	struct nvbios vbios;
 
 	struct nv04_mode_state mode_reg;
diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c
index 02a0151b0738..37615a447a4a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_object.c
+++ b/drivers/gpu/drm/nouveau/nouveau_object.c
@@ -93,7 +93,9 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
 	kref_init(&gpuobj->refcount);
 	gpuobj->size = size;
 
+	spin_lock(&dev_priv->ramin_lock);
 	list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
+	spin_unlock(&dev_priv->ramin_lock);
 
 	if (chan) {
 		NV_DEBUG(dev, "channel heap\n");
@@ -117,9 +119,22 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
 		}
 
 		/* try and get aperture space */
-		ramin = drm_mm_search_free(&dev_priv->ramin_heap, size, align, 0);
-		if (ramin)
-			ramin = drm_mm_get_block(ramin, size, align);
+		do {
+			if (drm_mm_pre_get(&dev_priv->ramin_heap))
+				return -ENOMEM;
+
+			spin_lock(&dev_priv->ramin_lock);
+			ramin = drm_mm_search_free(&dev_priv->ramin_heap, size,
+						   align, 0);
+			if (ramin == NULL) {
+				spin_unlock(&dev_priv->ramin_lock);
+				nouveau_gpuobj_ref(NULL, &gpuobj);
+				return ret;
+			}
+
+			ramin = drm_mm_get_block_atomic(ramin, size, align);
+			spin_unlock(&dev_priv->ramin_lock);
+		} while (ramin == NULL);
 
 		/* on nv50 it's ok to fail, we have a fallback path */
 		if (!ramin && dev_priv->card_type < NV_50) {
@@ -226,10 +241,11 @@ nouveau_gpuobj_del(struct kref *ref)
 	if (gpuobj->im_backing)
 		engine->instmem.clear(dev, gpuobj);
 
+	spin_lock(&dev_priv->ramin_lock);
 	if (gpuobj->im_pramin)
 		drm_mm_put_block(gpuobj->im_pramin);
-
 	list_del(&gpuobj->list);
+	spin_unlock(&dev_priv->ramin_lock);
 
 	kfree(gpuobj);
 }
@@ -276,7 +292,9 @@ nouveau_gpuobj_new_fake(struct drm_device *dev, u32 pinst, u64 vinst,
 		dev_priv->engine.instmem.flush(dev);
 	}
 
+	spin_lock(&dev_priv->ramin_lock);
 	list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
+	spin_unlock(&dev_priv->ramin_lock);
 	*pgpuobj = gpuobj;
 	return 0;
 }
@@ -553,7 +571,9 @@ nouveau_gpuobj_sw_new(struct nouveau_channel *chan, int class,
 	kref_init(&gpuobj->refcount);
 	gpuobj->cinst = 0x40;
 
+	spin_lock(&dev_priv->ramin_lock);
 	list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
+	spin_unlock(&dev_priv->ramin_lock);
 	*gpuobj_ret = gpuobj;
 	return 0;
 }
-- 
GitLab