From f2c03ecae627df77db25391fe85fcd8a2a4bdc0c Mon Sep 17 00:00:00 2001
From: Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
Date: Wed, 27 Sep 2006 19:07:55 +0200
Subject: Fix racy buffer object destruction.

---
 linux-core/drm_bo.c | 64 ++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 54 insertions(+), 10 deletions(-)

(limited to 'linux-core')

diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c
index f479c81a..6a3a5020 100644
--- a/linux-core/drm_bo.c
+++ b/linux-core/drm_bo.c
@@ -93,6 +93,15 @@ static void drm_bo_destroy_locked(drm_device_t * dev, drm_buffer_object_t * bo)
 
 	DRM_FLAG_MASKED(bo->priv_flags, 0, _DRM_BO_FLAG_UNFENCED);
 
+	/*
+	 * Somone might try to access us through the still active BM lists.
+	 */
+
+	if (atomic_read(&bo->usage) != 0) 
+		return;
+	if (!list_empty(&bo->ddestroy))
+		return;
+
 	if (bo->fence) {
 		if (!drm_fence_object_signaled(bo->fence, bo->fence_type)) {
 
@@ -114,6 +123,11 @@ static void drm_bo_destroy_locked(drm_device_t * dev, drm_buffer_object_t * bo)
 	list_del_init(&bo->head);
 
 	if (bo->tt) {
+
+		/*
+		 * This temporarily unlocks struct_mutex. 
+		 */
+
 		drm_unbind_ttm_region(bo->ttm_region);
 		drm_mm_put_block(&bm->tt_manager, bo->tt);
 		bo->tt = NULL;
@@ -136,33 +150,61 @@ static void drm_bo_delayed_delete(drm_device_t * dev)
 {
 	drm_buffer_manager_t *bm = &dev->bm;
 
-	drm_buffer_object_t *entry, *next;
+	drm_buffer_object_t *entry, *nentry;
+	struct list_head *list, *next;
 	drm_fence_object_t *fence;
 
 	mutex_lock(&dev->struct_mutex);
+	list = bm->ddestroy.next;
+	list_for_each_safe(list, next, &bm->ddestroy) {
+		entry = list_entry(list, drm_buffer_object_t, ddestroy);
+		nentry = NULL;
 
-	/*
-	 * FIXME: Lock buffer object mutex.
-	 */
+		/*
+		 * Another process may claim access to this entry through the
+		 * lru lists. In that case, just skip it.
+		 */
 
-	list_for_each_entry_safe(entry, next, &bm->ddestroy, ddestroy) {
-		fence = entry->fence;
+		if (atomic_read(&entry->usage) != 0)
+			continue;
+			
+		/*
+		 * Since we're the only users, No need to take the 
+		 * bo->mutex to watch the fence.
+		 */
 
+		fence = entry->fence;
 		if (fence && drm_fence_object_signaled(fence,
 						       entry->fence_type)) {
 			drm_fence_usage_deref_locked(dev, fence);
 			entry->fence = NULL;
 		}
+
 		if (!entry->fence) {
+
+			/*
+			 * Protect the "next" entry against destruction in case
+			 * drm_bo_destroy_locked temporarily releases the
+			 * struct_mutex;
+			 */
+			
+			nentry = NULL;		
+			if (next != &bm->ddestroy) {
+				nentry = list_entry(next, drm_buffer_object_t, 
+						    ddestroy);
+				atomic_inc(&nentry->usage);
+			}
 			DRM_DEBUG("Destroying delayed buffer object\n");
-			list_del(&entry->ddestroy);
+			list_del_init(&entry->ddestroy);
 			drm_bo_destroy_locked(dev, entry);
-		}
+			if (next != &bm->ddestroy)
+				atomic_dec(&nentry->usage);
+		} 
 	}
-
 	mutex_unlock(&dev->struct_mutex);
 }
 
+
 static void drm_bo_delayed_workqueue(void *data)
 {
 	drm_device_t *dev = (drm_device_t *) data;
@@ -1212,7 +1254,7 @@ int drm_buffer_object_create(drm_file_t * priv,
 	uint32_t new_flags;
 	unsigned long num_pages;
 
-	//	drm_bo_delayed_delete(dev);
+	drm_bo_delayed_delete(dev);
 
 	if ((buffer_start & ~PAGE_MASK) && (type != drm_bo_type_fake)) {
 		DRM_ERROR("Invalid buffer object start.\n");
@@ -1241,6 +1283,8 @@ int drm_buffer_object_create(drm_file_t * priv,
 	bo->dev = dev;
 	bo->type = type;
 	bo->num_pages = num_pages;
+	bo->vram = NULL;
+	bo->tt = NULL;
 	if (bo->type == drm_bo_type_fake) {
 		bo->offset = buffer_start;
 		bo->buffer_start = 0;
-- 
cgit v1.2.3