ttm code cleanup.

Fix the sleep-in-page-table-spinlock bug discovered by Dave Airlie
This commit is contained in:
Thomas Hellstrom 2006-08-22 11:19:53 +02:00
parent a6535c8db4
commit ca4e34e532
3 changed files with 75 additions and 68 deletions

View file

@ -156,7 +156,8 @@
#define DRM_MEM_HASHTAB 23
#define DRM_MEM_OBJECTS 24
#define DRM_MEM_FENCE 25
#define DRM_MEM_TTM 26
#define DRM_MEM_BUFOBJ 27
#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8)
#define DRM_MAP_HASH_OFFSET 0x10000000

View file

@ -1,6 +1,6 @@
/**************************************************************************
*
* Copyright 2006 Tungsten Graphics, Inc., Steamboat Springs, CO.
* Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@ -60,7 +60,7 @@ int drm_ttm_add_mm_to_list(drm_ttm_t * ttm, struct mm_struct *mm)
} else if ((unsigned long)mm < (unsigned long)entry->mm) ;
}
n_entry = drm_alloc(sizeof(*n_entry), DRM_MEM_MM);
n_entry = drm_alloc(sizeof(*n_entry), DRM_MEM_TTM);
if (!entry) {
DRM_ERROR("Allocation of process mm pointer entry failed\n");
return -ENOMEM;
@ -83,7 +83,7 @@ void drm_ttm_delete_mm(drm_ttm_t * ttm, struct mm_struct *mm)
if (mm == entry->mm) {
if (atomic_add_negative(-1, &entry->refcount)) {
list_del(&entry->head);
drm_free(entry, sizeof(*entry), DRM_MEM_MM);
drm_free(entry, sizeof(*entry), DRM_MEM_TTM);
atomic_dec(&ttm->shared_count);
ttm->mm_list_seq++;
}
@ -155,7 +155,9 @@ static int unmap_vma_pages(drm_ttm_t * ttm, unsigned long page_offset,
struct page **first_page = ttm->pages + page_offset;
struct page **last_page = ttm->pages + (page_offset + num_pages);
struct page **cur_page;
#if !defined(flush_tlb_mm) && defined(MODULE)
int flush_tlb = 0;
#endif
list_for_each(list, &ttm->vma_list->head) {
drm_ttm_vma_list_t *entry =
list_entry(list, drm_ttm_vma_list_t, head);
@ -164,7 +166,14 @@ static int unmap_vma_pages(drm_ttm_t * ttm, unsigned long page_offset,
(page_offset << PAGE_SHIFT),
entry->vma->vm_start +
((page_offset + num_pages) << PAGE_SHIFT));
#if !defined(flush_tlb_mm) && defined(MODULE)
flush_tlb = 1;
#endif
}
#if !defined(flush_tlb_mm) && defined(MODULE)
if (flush_tlb)
global_flush_tlb();
#endif
for (cur_page = first_page; cur_page != last_page; ++cur_page) {
if (page_mapcount(*cur_page) != 0) {
@ -193,33 +202,19 @@ int drm_destroy_ttm(drm_ttm_t * ttm)
if (atomic_read(&ttm->vma_count) > 0) {
DRM_DEBUG("VMAs are still alive. Skipping destruction.\n");
return -EBUSY;
} else {
DRM_DEBUG("Checking for busy regions.\n");
}
}
if (ttm->be_list) {
list_for_each_safe(list, next, &ttm->be_list->head) {
drm_ttm_backend_list_t *entry =
list_entry(list, drm_ttm_backend_list_t, head);
#ifdef REMOVED
drm_ht_remove_item(&ttm->dev->ttmreghash,
&entry->hash);
#endif
drm_destroy_ttm_region(entry);
}
drm_free(ttm->be_list, sizeof(*ttm->be_list), DRM_MEM_MAPS);
drm_free(ttm->be_list, sizeof(*ttm->be_list), DRM_MEM_TTM);
ttm->be_list = NULL;
}
if (atomic_read(&ttm->unfinished_regions) > 0) {
DRM_DEBUG("Regions are still busy. Skipping destruction.\n");
ttm->destroy = TRUE;
return -EAGAIN;
} else {
DRM_DEBUG("About to really destroy ttm.\n");
}
if (ttm->pages) {
for (i = 0; i < ttm->num_pages; ++i) {
cur_page = ttm->pages + i;
@ -237,6 +232,7 @@ int drm_destroy_ttm(drm_ttm_t * ttm)
vfree(ttm->pages);
ttm->pages = NULL;
}
if (ttm->page_flags) {
vfree(ttm->page_flags);
ttm->page_flags = NULL;
@ -248,12 +244,13 @@ int drm_destroy_ttm(drm_ttm_t * ttm)
list_entry(list, drm_ttm_vma_list_t, head);
list_del(list);
entry->vma->vm_private_data = NULL;
drm_free(entry, sizeof(*entry), DRM_MEM_MAPS);
drm_free(entry, sizeof(*entry), DRM_MEM_TTM);
}
drm_free(ttm->vma_list, sizeof(*ttm->vma_list), DRM_MEM_MAPS);
drm_free(ttm->vma_list, sizeof(*ttm->vma_list), DRM_MEM_TTM);
ttm->vma_list = NULL;
}
drm_free(ttm, sizeof(*ttm), DRM_MEM_MAPS);
drm_free(ttm, sizeof(*ttm), DRM_MEM_TTM);
return 0;
}
@ -271,14 +268,12 @@ drm_ttm_t *drm_init_ttm(struct drm_device * dev, unsigned long size)
if (!dev->driver->bo_driver)
return NULL;
ttm = drm_calloc(1, sizeof(*ttm), DRM_MEM_MAPS);
ttm = drm_calloc(1, sizeof(*ttm), DRM_MEM_TTM);
if (!ttm)
return NULL;
ttm->lhandle = 0;
atomic_set(&ttm->vma_count, 0);
atomic_set(&ttm->unfinished_regions, 0);
ttm->destroy = FALSE;
ttm->num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
ttm->page_flags = vmalloc(ttm->num_pages * sizeof(*ttm->page_flags));
@ -297,7 +292,7 @@ drm_ttm_t *drm_init_ttm(struct drm_device * dev, unsigned long size)
}
memset(ttm->pages, 0, ttm->num_pages * sizeof(*ttm->pages));
ttm->be_list = drm_calloc(1, sizeof(*ttm->be_list), DRM_MEM_MAPS);
ttm->be_list = drm_calloc(1, sizeof(*ttm->be_list), DRM_MEM_TTM);
if (!ttm->be_list) {
DRM_ERROR("Alloc be regions failed\n");
drm_destroy_ttm(ttm);
@ -309,7 +304,7 @@ drm_ttm_t *drm_init_ttm(struct drm_device * dev, unsigned long size)
atomic_set(&ttm->shared_count, 0);
ttm->mm_list_seq = 0;
ttm->vma_list = drm_calloc(1, sizeof(*ttm->vma_list), DRM_MEM_MAPS);
ttm->vma_list = drm_calloc(1, sizeof(*ttm->vma_list), DRM_MEM_TTM);
if (!ttm->vma_list) {
DRM_ERROR("Alloc vma list failed\n");
drm_destroy_ttm(ttm);
@ -350,10 +345,10 @@ static int drm_ttm_lock_mmap_sem(drm_ttm_t * ttm)
if (shared_count > cur_count) {
if (mm_list)
drm_free(mm_list, sizeof(*mm_list) * cur_count,
DRM_MEM_MM);
DRM_MEM_TTM);
cur_count = shared_count + 10;
mm_list =
drm_alloc(sizeof(*mm_list) * cur_count, DRM_MEM_MM);
drm_alloc(sizeof(*mm_list) * cur_count, DRM_MEM_TTM);
if (!mm_list)
return -ENOMEM;
}
@ -383,9 +378,8 @@ static int drm_ttm_lock_mmap_sem(drm_ttm_t * ttm)
} while (list_seq != ttm->mm_list_seq);
if (mm_list)
drm_free(mm_list, sizeof(*mm_list) * cur_count, DRM_MEM_MM);
drm_free(mm_list, sizeof(*mm_list) * cur_count, DRM_MEM_TTM);
ttm->mmap_sem_locked = TRUE;
return 0;
}
@ -403,6 +397,7 @@ static int drm_set_caching(drm_ttm_t * ttm, unsigned long page_offset,
drm_ttm_lock_mm(ttm, FALSE, TRUE);
unmap_vma_pages(ttm, page_offset, num_pages);
drm_ttm_unlock_mm(ttm, FALSE, TRUE);
for (i = 0; i < num_pages; ++i) {
cur = page_offset + i;
@ -413,7 +408,6 @@ static int drm_set_caching(drm_ttm_t * ttm, unsigned long page_offset,
&& page_address(*cur_page) != NULL) {
DRM_ERROR
("Illegal mapped HighMem Page\n");
drm_ttm_unlock_mm(ttm, FALSE, TRUE);
return -EINVAL;
}
} else if ((ttm->page_flags[cur] &
@ -426,12 +420,9 @@ static int drm_set_caching(drm_ttm_t * ttm, unsigned long page_offset,
}
if (do_tlbflush)
global_flush_tlb();
drm_ttm_unlock_mm(ttm, FALSE, TRUE);
return 0;
}
/*
* Unbind a ttm region from the aperture.
*/
@ -508,7 +499,7 @@ void drm_destroy_ttm_region(drm_ttm_backend_list_t * entry)
cur_page_flags++;
}
drm_free(entry, sizeof(*entry), DRM_MEM_MAPS);
drm_free(entry, sizeof(*entry), DRM_MEM_TTM);
}
/*
@ -541,13 +532,13 @@ int drm_create_ttm_region(drm_ttm_t * ttm, unsigned long page_offset,
}
}
entry = drm_calloc(1, sizeof(*entry), DRM_MEM_MAPS);
entry = drm_calloc(1, sizeof(*entry), DRM_MEM_TTM);
if (!entry)
return -ENOMEM;
be = ttm->dev->driver->bo_driver->create_ttm_backend_entry(ttm->dev, cached);
if (!be) {
drm_free(entry, sizeof(*entry), DRM_MEM_MAPS);
drm_free(entry, sizeof(*entry), DRM_MEM_TTM);
DRM_ERROR("Couldn't create backend.\n");
return -EINVAL;
}
@ -661,7 +652,7 @@ void drm_user_destroy_region(drm_ttm_backend_list_t * entry)
be = entry->be;
if (!be) {
drm_free(entry, sizeof(*entry), DRM_MEM_MAPS);
drm_free(entry, sizeof(*entry), DRM_MEM_TTM);
return;
}
@ -679,7 +670,7 @@ void drm_user_destroy_region(drm_ttm_backend_list_t * entry)
}
be->destroy(be);
drm_free(entry, sizeof(*entry), DRM_MEM_MAPS);
drm_free(entry, sizeof(*entry), DRM_MEM_TTM);
return;
}
@ -703,7 +694,7 @@ int drm_user_create_region(drm_device_t * dev, unsigned long start, int len,
if (!dev->driver->bo_driver->create_ttm_backend_entry)
return -EFAULT;
tmp = drm_calloc(1, sizeof(*tmp), DRM_MEM_MAPS);
tmp = drm_calloc(1, sizeof(*tmp), DRM_MEM_TTM);
if (!tmp)
return -ENOMEM;
@ -748,9 +739,6 @@ int drm_user_create_region(drm_device_t * dev, unsigned long start, int len,
}
tmp->state = ttm_unbound;
#ifdef REMOVED
tmp->mm = &dev->driver->bo_driver->ttm_mm;
#endif
*entry = tmp;
return 0;
@ -766,7 +754,7 @@ int drm_add_ttm(drm_device_t * dev, unsigned size, drm_map_list_t ** maplist)
drm_map_t *map;
drm_ttm_t *ttm;
map = drm_alloc(sizeof(*map), DRM_MEM_MAPS);
map = drm_alloc(sizeof(*map), DRM_MEM_TTM);
if (!map)
return -ENOMEM;
@ -774,7 +762,7 @@ int drm_add_ttm(drm_device_t * dev, unsigned size, drm_map_list_t ** maplist)
if (!ttm) {
DRM_ERROR("Could not create ttm\n");
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
drm_free(map, sizeof(*map), DRM_MEM_TTM);
return -ENOMEM;
}
@ -783,25 +771,23 @@ int drm_add_ttm(drm_device_t * dev, unsigned size, drm_map_list_t ** maplist)
map->flags = _DRM_REMOVABLE;
map->size = size;
list = drm_calloc(1, sizeof(*list), DRM_MEM_MAPS);
list = drm_calloc(1, sizeof(*list), DRM_MEM_TTM);
if (!list) {
drm_destroy_ttm(ttm);
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
drm_free(map, sizeof(*map), DRM_MEM_TTM);
return -ENOMEM;
}
map->handle = (void *)list;
#ifdef REMOVED
if (drm_ht_just_insert_please(&dev->maphash, &list->hash,
if (drm_ht_just_insert_please(&dev->map_hash, &list->hash,
(unsigned long) map->handle,
32 - PAGE_SHIFT)) {
32 - PAGE_SHIFT - 3, PAGE_SHIFT,
DRM_MAP_HASH_OFFSET)) {
drm_destroy_ttm(ttm);
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
drm_free(list, sizeof(*list), DRM_MEM_MAPS);
drm_free(map, sizeof(*map), DRM_MEM_TTM);
drm_free(list, sizeof(*list), DRM_MEM_TTM);
return -ENOMEM;
}
#endif
list->user_token =
(list->hash.key << PAGE_SHIFT) + DRM_MAP_HASH_OFFSET;

View file

@ -1,3 +1,34 @@
/**************************************************************************
*
* Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
*
**************************************************************************/
/*
* Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
*/
#ifndef _DRM_TTM_H
#define _DRM_TTM_H
#define DRM_HAS_TTM
@ -30,9 +61,7 @@ typedef struct drm_ttm_backend {
#define DRM_FLUSH_EXE (0x04)
typedef struct drm_ttm_backend_list {
drm_hash_item_t hash;
uint32_t flags;
atomic_t refcount;
struct list_head head;
drm_ttm_backend_t *be;
unsigned page_offset;
@ -68,9 +97,6 @@ typedef struct drm_ttm {
struct drm_device *dev;
drm_ttm_backend_list_t *be_list;
atomic_t vma_count;
atomic_t unfinished_regions;
drm_file_t *owner;
int destroy;
int mmap_sem_locked;
} drm_ttm_t;
@ -127,12 +153,6 @@ extern int drm_destroy_ttm(drm_ttm_t * ttm);
extern void drm_user_destroy_region(drm_ttm_backend_list_t * entry);
extern int drm_ttm_add_mm_to_list(drm_ttm_t * ttm, struct mm_struct *mm);
extern void drm_ttm_delete_mm(drm_ttm_t * ttm, struct mm_struct *mm);
extern void drm_ttm_fence_before_destroy(drm_ttm_t * ttm);
extern void drm_fence_unfenced_region(drm_ttm_backend_list_t * entry);
extern int drm_ttm_ioctl(DRM_IOCTL_ARGS);
extern int drm_mm_init_ioctl(DRM_IOCTL_ARGS);
extern int drm_mm_fence_ioctl(DRM_IOCTL_ARGS);
#define DRM_MASK_VAL(dest, mask, val) \
(dest) = ((dest) & ~(mask)) | ((val) & (mask));