From f2c03ecae627df77db25391fe85fcd8a2a4bdc0c Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom 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/drm_bo.c') 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