diff options
author | Thomas Hellstrom <thomas@tungstengraphics.com> | 2006-03-16 15:41:26 +0000 |
---|---|---|
committer | Thomas Hellstrom <thomas@tungstengraphics.com> | 2006-03-16 15:41:26 +0000 |
commit | 1b89b93deecdb6387637f08dea02672fb9987abe (patch) | |
tree | 11bad373f0a5e82daf1f0ffb1a35e8c1da74d636 | |
parent | 8ab438f5e73a4ad67fd0f82ca25da7756cf8274a (diff) |
Delayed buffer destruction. Set aperture size to 32M.
-rw-r--r-- | linux-core/drmP.h | 2 | ||||
-rw-r--r-- | linux-core/drm_fops.c | 3 | ||||
-rw-r--r-- | linux-core/drm_ttm.c | 171 | ||||
-rw-r--r-- | linux-core/drm_ttm.h | 3 | ||||
-rw-r--r-- | linux-core/drm_vm.c | 7 | ||||
-rw-r--r-- | shared-core/drm.h | 1 | ||||
-rw-r--r-- | shared-core/i915_dma.c | 2 |
7 files changed, 133 insertions, 56 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 8c30fce6..6bde2db8 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -583,6 +583,7 @@ typedef struct drm_ttm_mm { struct drm_device *dev; drm_mm_t mm; struct list_head lru_head; + struct list_head delayed; } drm_ttm_mm_t; typedef struct drm_mm_driver { @@ -1131,6 +1132,7 @@ extern void drm_sysfs_device_remove(struct class_device *class_dev); /* ttm support (drm_ttm.c) */ extern int drm_add_ttm(drm_device_t *dev, unsigned size, drm_map_list_t **maplist); +extern int drm_ttm_destroy_delayed(drm_ttm_mm_t *mm, int ret_if_busy); /* * Basic memory manager support (drm_mm.c) diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c index 5299043e..88070cb4 100644 --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -477,6 +477,9 @@ int drm_release(struct inode *inode, struct file *filp) dev->file_last = priv->prev; } + if (dev->mm_driver) + drm_ttm_destroy_delayed(&dev->mm_driver->ttm_mm, TRUE); + list_for_each_safe(list, next, &priv->ttms) { drm_map_list_t *entry = list_entry(list, drm_map_list_t, head); list_del(list); diff --git a/linux-core/drm_ttm.c b/linux-core/drm_ttm.c index 0f63963c..27144ec0 100644 --- a/linux-core/drm_ttm.c +++ b/linux-core/drm_ttm.c @@ -70,8 +70,7 @@ void pmd_clear_bad(pmd_t * pmd) static void change_pte_range(struct mm_struct *mm, pmd_t * pmd, unsigned long addr, unsigned long end, - pgprot_t newprot, unsigned long pfn, - int flags) + pgprot_t newprot, unsigned long pfn, int flags) { pte_t *pte; @@ -80,7 +79,7 @@ static void change_pte_range(struct mm_struct *mm, pmd_t * pmd, if ((flags & DRM_TTM_UNMAP) && pte_present(*pte)) { pte_t ptent; ptent = *pte; - ptep_get_and_clear(mm, addr,pte); + ptep_get_and_clear(mm, addr, pte); lazy_mmu_prot_update(ptent); } if (flags & DRM_TTM_REWRITE) { @@ -97,7 +96,7 @@ static void change_pte_range(struct mm_struct *mm, pmd_t * pmd, static inline void change_pmd_range(struct mm_struct *mm, pud_t * pud, unsigned long addr, unsigned long end, - pgprot_t newprot, unsigned long pfn, + pgprot_t newprot, unsigned long pfn, int flags) { pmd_t *pmd; @@ -114,7 +113,7 @@ static inline void change_pmd_range(struct mm_struct *mm, pud_t * pud, static inline void change_pud_range(struct mm_struct *mm, pgd_t * pgd, unsigned long addr, unsigned long end, - pgprot_t newprot, unsigned long pfn, + pgprot_t newprot, unsigned long pfn, int flags) { pud_t *pud; @@ -123,7 +122,7 @@ static inline void change_pud_range(struct mm_struct *mm, pgd_t * pgd, pud = pud_offset(pgd, addr); do { next = pud_addr_end(addr, end); - if (pud_none_or_clear_bad(pud)) + if (pud_none_or_clear_bad(pud)) continue; change_pmd_range(mm, pud, addr, next, newprot, pfn, flags); } while (pud++, addr = next, addr != end); @@ -155,7 +154,6 @@ static void drm_change_protection(struct vm_area_struct *vma, * End linux mm subsystem code. */ - static int ioremap_vmas(drm_ttm_t * ttm, unsigned long page_offset, unsigned long num_pages, unsigned long aper_offset) { @@ -164,22 +162,21 @@ static int ioremap_vmas(drm_ttm_t * ttm, unsigned long page_offset, list_for_each(list, &ttm->vma_list->head) { drm_ttm_vma_list_t *entry = - list_entry(list, drm_ttm_vma_list_t, head); - - ret = io_remap_pfn_range(entry->vma, + list_entry(list, drm_ttm_vma_list_t, head); + + ret = io_remap_pfn_range(entry->vma, entry->vma->vm_start + (page_offset << PAGE_SHIFT), - (ttm->aperture_base >> PAGE_SHIFT) + aper_offset, - num_pages << PAGE_SHIFT, + (ttm->aperture_base >> PAGE_SHIFT) + + aper_offset, num_pages << PAGE_SHIFT, drm_io_prot(_DRM_AGP, entry->vma)); - if (ret) + if (ret) break; } global_flush_tlb(); return ret; } - /* * Unmap all vma pages from vmas mapping this ttm. */ @@ -200,7 +197,7 @@ 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), - entry->vma->vm_page_prot, 0, + entry->vma->vm_page_prot, 0, DRM_TTM_UNMAP); } @@ -231,9 +228,9 @@ 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 -1; + return -EBUSY; } else { - DRM_DEBUG("About to really destroy ttm.\n"); + DRM_DEBUG("Checking for busy regions.\n"); } if (ttm->be_list) { @@ -250,6 +247,14 @@ int drm_destroy_ttm(drm_ttm_t * ttm) drm_free(ttm->be_list, sizeof(*ttm->be_list), DRM_MEM_MAPS); } + 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; @@ -304,6 +309,8 @@ drm_ttm_t *drm_init_ttm(struct drm_device * dev, unsigned long size) 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)); @@ -402,31 +409,41 @@ static int drm_set_caching(drm_ttm_t * ttm, unsigned long page_offset, * Take a ttm region out of the aperture manager. */ -static void remove_ttm_region(drm_ttm_backend_list_t * entry) +static int remove_ttm_region(drm_ttm_backend_list_t * entry, int ret_if_busy) { drm_mm_node_t *mm_node = entry->mm_node; drm_ttm_mm_priv_t *mm_priv; drm_ttm_mm_t *mm = entry->mm; drm_device_t *dev = mm->dev; + int ret; if (!mm_node) - return; + return 0; entry->mm_node = NULL; mm_priv = (drm_ttm_mm_priv_t *) mm_node->private; if (!mm_priv) - return; + return 0; + + if (mm_priv->fence_valid) { + if (ret_if_busy + && !dev->mm_driver->test_fence(mm->dev, entry->fence_type, + mm_priv->fence)) + return -EBUSY; + ret = dev->mm_driver->wait_fence(mm->dev, entry->fence_type, + mm_priv->fence); + if (ret) + return ret; + } - if (mm_priv->fence_valid) - dev->mm_driver->wait_fence(mm->dev, entry->fence_type, - mm_priv->fence); mm_node->private = NULL; spin_lock(&mm->mm.mm_lock); list_del(&mm_priv->lru); drm_mm_put_block_locked(&mm->mm, mm_node); spin_unlock(&mm->mm.mm_lock); drm_free(mm_priv, sizeof(*mm_priv), DRM_MEM_MM); + return 0; } /* @@ -462,12 +479,46 @@ int drm_evict_ttm_region(drm_ttm_backend_list_t * entry) void drm_unbind_ttm_region(drm_ttm_backend_list_t * entry) { - remove_ttm_region(entry); + remove_ttm_region(entry, FALSE); drm_evict_ttm_region(entry); entry->state = ttm_unbound; } /* + * This should be called every once in a while to destroy regions and ttms that are + * put on hold for destruction because their fence had not retired. + */ + +int drm_ttm_destroy_delayed(drm_ttm_mm_t * mm, int ret_if_busy) +{ + struct list_head *list, *next; + drm_ttm_t *ttm; + + list_for_each_safe(list, next, &mm->delayed) { + drm_ttm_backend_list_t *entry = + list_entry(list, drm_ttm_backend_list_t, head); + if (!remove_ttm_region(entry, ret_if_busy)) + continue; + + ttm = entry->owner; + if (ttm) { + DRM_DEBUG("Destroying put-on-hold region\n"); + drm_destroy_ttm_region(entry); + atomic_dec(&ttm->unfinished_regions); + if (ttm->destroy + && atomic_read(&ttm->unfinished_regions) == 0) { + DRM_DEBUG("Destroying put-on-hold ttm\n"); + drm_destroy_ttm(ttm); + + } + } else { + drm_user_destroy_region(entry); + } + } + return (mm->delayed.next == &mm->delayed); +} + +/* * Destroy and clean up all resources associated with a ttm region. * FIXME: release pages to OS when doing this operation. */ @@ -479,8 +530,15 @@ void drm_destroy_ttm_region(drm_ttm_backend_list_t * entry) uint32_t *cur_page_flags; int i; - list_del(&entry->head); - remove_ttm_region(entry); + list_del_init(&entry->head); + + if (remove_ttm_region(entry, TRUE)) { + DRM_DEBUG("Putting destruction on hold\n"); + atomic_inc(&ttm->unfinished_regions); + list_add_tail(&entry->head, &entry->mm->delayed); + return; + } + drm_unbind_ttm_region(entry); if (be) { be->clear(entry->be); @@ -556,7 +614,7 @@ int drm_create_ttm_region(drm_ttm_t * ttm, unsigned long page_offset, for (i = 0; i < entry->num_pages; ++i) { cur_page = ttm->pages + (page_offset + i); if (!*cur_page) { - *cur_page = alloc_page(GFP_USER); + *cur_page = alloc_page(GFP_KERNEL); if (!*cur_page) { DRM_ERROR("Page allocation failed\n"); drm_destroy_ttm_region(entry); @@ -574,7 +632,7 @@ int drm_create_ttm_region(drm_ttm_t * ttm, unsigned long page_offset, entry->mm_node = NULL; entry->mm = &ttm->dev->mm_driver->ttm_mm; ttm->aperture_base = be->aperture_base; - + *region = entry; return 0; } @@ -594,7 +652,7 @@ int drm_bind_ttm_region(drm_ttm_backend_list_t * region, int i; uint32_t *cur_page_flag; - int ret = 0; + int ret = 0; drm_ttm_backend_t *be; drm_ttm_t *ttm; @@ -605,10 +663,10 @@ int drm_bind_ttm_region(drm_ttm_backend_list_t * region, ttm = region->owner; if (ttm && be->needs_cache_adjust(be)) { - drm_set_caching(ttm, region->page_offset, region->num_pages, - DRM_TTM_PAGE_UNCACHED, 0); - ioremap_vmas(ttm, region->page_offset, region->num_pages, - aper_offset); + drm_set_caching(ttm, region->page_offset, region->num_pages, + DRM_TTM_PAGE_UNCACHED, FALSE); + ioremap_vmas(ttm, region->page_offset, region->num_pages, + aper_offset); } if ((ret = be->bind(be, aper_offset))) { @@ -648,7 +706,10 @@ void drm_user_destroy_region(drm_ttm_backend_list_t * entry) if (!entry || entry->owner) return; - remove_ttm_region(entry); + if (remove_ttm_region(entry, TRUE)) { + list_add_tail(&entry->head, &entry->mm->delayed); + return; + } be = entry->be; if (!be) { @@ -671,6 +732,7 @@ void drm_user_destroy_region(drm_ttm_backend_list_t * entry) be->destroy(be); drm_free(entry, sizeof(*entry), DRM_MEM_MAPS); + return; } /* @@ -915,7 +977,8 @@ typedef struct drm_val_action { static int drm_validate_ttm_region(drm_ttm_backend_list_t * entry, uint32_t fence_type, unsigned *aper_offset, - drm_val_action_t * action, uint32_t validation_seq) + drm_val_action_t * action, + uint32_t validation_seq) { drm_mm_node_t *mm_node = entry->mm_node; drm_ttm_mm_t *mm = entry->mm; @@ -936,6 +999,7 @@ static int drm_validate_ttm_region(drm_ttm_backend_list_t * entry, num_pages = (entry->owner) ? entry->num_pages : entry->anon_locked; spin_lock(mm_lock); while (!mm_node) { + drm_ttm_destroy_delayed(entry->mm, TRUE); mm_node = drm_mm_search_free_locked(&entry->mm->mm, num_pages, 0, 0); if (!mm_node) { @@ -988,11 +1052,21 @@ static void drm_ttm_mm_init(drm_device_t * dev, drm_ttm_mm_t * mm, { drm_mm_init(&mm->mm, start, size); INIT_LIST_HEAD(&mm->lru_head); + INIT_LIST_HEAD(&mm->delayed); mm->dev = dev; } static void drm_ttm_mm_takedown(drm_ttm_mm_t * mm) { + if (drm_ttm_destroy_delayed(mm, FALSE)) { + DRM_ERROR("DRM_MM: Inconsistency: " + "There are still used buffers\n"); + + /* + * FIXME: Clean this up in a nice way. + */ + + } drm_mm_takedown(&mm->mm); } @@ -1122,7 +1196,8 @@ static int drm_ttm_create_user_buf(drm_ttm_buf_arg_t * buf_p, } static void drm_ttm_handle_buf(drm_file_t * priv, drm_ttm_buf_arg_t * buf_p, - drm_val_action_t * action, uint32_t validation_seq) + drm_val_action_t * action, + uint32_t validation_seq) { drm_device_t *dev = priv->head->dev; drm_ttm_t *ttm; @@ -1143,7 +1218,8 @@ static void drm_ttm_handle_buf(drm_file_t * priv, drm_ttm_buf_arg_t * buf_p, } buf_p->ret = drm_validate_ttm_region(entry, buf_p->fence_type, - &buf_p->aper_offset, action, validation_seq); + &buf_p->aper_offset, action, + validation_seq); break; case ttm_validate: buf_p->ret = @@ -1170,7 +1246,8 @@ static void drm_ttm_handle_buf(drm_file_t * priv, drm_ttm_buf_arg_t * buf_p, } buf_p->ret = drm_validate_ttm_region(entry, buf_p->fence_type, - &buf_p->aper_offset, action, validation_seq); + &buf_p->aper_offset, action, + validation_seq); break; case ttm_unbind: buf_p->ret = @@ -1180,20 +1257,13 @@ static void drm_ttm_handle_buf(drm_file_t * priv, drm_ttm_buf_arg_t * buf_p, break; drm_unbind_ttm_region(entry); break; - case ttm_evict: - buf_p->ret = - drm_ttm_region_from_handle(buf_p->region_handle, priv, - &entry); - if (buf_p->ret) - break; - remove_ttm_region(entry); - buf_p->ret = drm_evict_ttm_region(entry); - break; case ttm_destroy: buf_p->ret = drm_ttm_region_from_handle(buf_p->region_handle, priv, &entry); + drm_remove_ht_val(&dev->ttmreghash, buf_p->region_handle); + ttm_mm = entry->mm; if (buf_p->ret) break; if (entry->owner) { @@ -1202,7 +1272,7 @@ static void drm_ttm_handle_buf(drm_file_t * priv, drm_ttm_buf_arg_t * buf_p, list_del(&entry->head); drm_user_destroy_region(entry); } - drm_remove_ht_val(&dev->ttmreghash, buf_p->region_handle); + drm_ttm_destroy_delayed(ttm_mm, TRUE); break; default: DRM_ERROR("Invalid TTM buffer operation\n"); @@ -1343,12 +1413,13 @@ static int drm_ttm_handle_remove(drm_file_t * priv, drm_handle_t handle) up(&dev->struct_sem); return ret; } - ret = drm_destroy_ttm(ttm); list_del(&map_list->head); drm_remove_ht_val(&dev->maphash, (handle - DRM_MAP_HASH_OFFSET) >> PAGE_SHIFT); + ret = drm_destroy_ttm(ttm); + drm_ttm_destroy_delayed(&dev->mm_driver->ttm_mm, TRUE); up(&dev->struct_sem); - if (!ret) { + if (ret != -EBUSY) { /* * No active VMAs. So OK to free map here. diff --git a/linux-core/drm_ttm.h b/linux-core/drm_ttm.h index 7e181bce..9bee63da 100644 --- a/linux-core/drm_ttm.h +++ b/linux-core/drm_ttm.h @@ -30,6 +30,7 @@ typedef struct drm_ttm_backend { #define DRM_FLUSH_EXE (0x04) typedef struct drm_ttm_backend_list { + atomic_t refcount; struct list_head head; drm_ttm_backend_t *be; unsigned page_offset; @@ -66,7 +67,9 @@ 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; } drm_ttm_t; /* diff --git a/linux-core/drm_vm.c b/linux-core/drm_vm.c index a214bc4e..d2a4328e 100644 --- a/linux-core/drm_vm.c +++ b/linux-core/drm_vm.c @@ -244,7 +244,7 @@ static int drm_ttm_remap_bound_pfn(struct vm_area_struct *vma, int bound_sequence = FALSE; int ret = 0; uint32_t cur_flags; - + for (i=page_offset; i<page_offset + num_pages; ++i) { cur_flags = ttm->page_flags[i]; @@ -332,7 +332,7 @@ static __inline__ struct page *drm_do_vm_ttm_nopage(struct vm_area_struct *vma, if (!page) { page = ttm->pages[page_offset] = - alloc_page(GFP_USER); + alloc_page(GFP_KERNEL); SetPageReserved(page); } if (!page) @@ -340,7 +340,6 @@ static __inline__ struct page *drm_do_vm_ttm_nopage(struct vm_area_struct *vma, get_page(page); - /* * FIXME: Potential security hazard: Have someone export the * mm subsystem's protection_map instead. Otherwise we will @@ -754,7 +753,7 @@ static void drm_vm_ttm_close(struct vm_area_struct *vma) found_maps++; } if (!found_maps) { - if (!drm_destroy_ttm(ttm)) { + if (drm_destroy_ttm(ttm) != -EBUSY) { drm_free(map, sizeof(*map), DRM_MEM_MAPS); } } diff --git a/shared-core/drm.h b/shared-core/drm.h index 3fafa012..acd1db5a 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -657,7 +657,6 @@ typedef struct drm_ttm_buf_arg { ttm_validate, ttm_validate_user, ttm_unbind, - ttm_evict, ttm_destroy, } op; drm_handle_t ttm_handle; diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c index 1d276dbe..2346a76c 100644 --- a/shared-core/i915_dma.c +++ b/shared-core/i915_dma.c @@ -205,7 +205,7 @@ static int i915_initialize(drm_device_t * dev, arg.req.vr_offset_lo = 1024*1024*8; arg.req.vr_offset_hi = 0; - arg.req.tt_p_size_lo = 1024*1024*48/4096; + arg.req.tt_p_size_lo = 1024*1024*32/4096; arg.req.tt_p_size_hi = 0; arg.req.tt_p_offset_lo = 1024*1024*128/4096; arg.req.tt_p_offset_hi = 0; |