diff options
Diffstat (limited to 'linux-core/drm_ttm.c')
-rw-r--r-- | linux-core/drm_ttm.c | 649 |
1 files changed, 141 insertions, 508 deletions
diff --git a/linux-core/drm_ttm.c b/linux-core/drm_ttm.c index 51e28ac4..297d4f71 100644 --- a/linux-core/drm_ttm.c +++ b/linux-core/drm_ttm.c @@ -27,20 +27,6 @@ **************************************************************************/ #include "drmP.h" -#include <asm/tlbflush.h> - -typedef struct p_mm_entry { - struct list_head head; - struct mm_struct *mm; - atomic_t refcount; -} p_mm_entry_t; - -typedef struct drm_val_action { - int needs_rx_flush; - int evicted_tt; - int evicted_vram; - int validated; -} drm_val_action_t; /* * Use kmalloc if possible. Otherwise fall back to vmalloc. @@ -75,20 +61,52 @@ static void ttm_free(void *pointer, unsigned long size, int type) * Unmap all vma pages from vmas mapping this ttm. */ -static int unmap_vma_pages(drm_ttm_t * ttm, unsigned long page_offset, - unsigned long num_pages) +static int unmap_vma_pages(drm_ttm_t * ttm) { drm_device_t *dev = ttm->dev; - loff_t offset = ((loff_t) ttm->mapping_offset + page_offset) - << PAGE_SHIFT; - loff_t holelen = num_pages << PAGE_SHIFT; + loff_t offset = ((loff_t) ttm->mapping_offset) << PAGE_SHIFT; + loff_t holelen = ((loff_t) ttm->num_pages) << PAGE_SHIFT; - unmap_mapping_range(dev->dev_mapping, offset, holelen, 1); return 0; } /* + * Change caching policy for the linear kernel map + * for range of pages in a ttm. + */ + +static int drm_set_caching(drm_ttm_t * ttm, int noncached) +{ + int i; + struct page **cur_page; + int do_tlbflush = 0; + + if ((ttm->page_flags & DRM_TTM_PAGE_UNCACHED) == noncached) + return 0; + + for (i = 0; i < ttm->num_pages; ++i) { + cur_page = ttm->pages + i; + if (*cur_page) { + if (!PageHighMem(*cur_page)) { + if (noncached) { + map_page_into_agp(*cur_page); + } else { + unmap_page_from_agp(*cur_page); + } + do_tlbflush = 1; + } + } + } + if (do_tlbflush) + flush_agp_mappings(); + + DRM_MASK_VAL(ttm->page_flags, DRM_TTM_PAGE_UNCACHED, noncached); + + return 0; +} + +/* * Free all resources associated with a ttm. */ @@ -96,8 +114,8 @@ int drm_destroy_ttm(drm_ttm_t * ttm) { int i; - struct list_head *list, *next; struct page **cur_page; + drm_ttm_backend_t *be; if (!ttm) return 0; @@ -110,30 +128,26 @@ int drm_destroy_ttm(drm_ttm_t * ttm) DRM_DEBUG("Destroying a ttm\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); - drm_destroy_ttm_region(entry); - } + be = ttm->be; - drm_free(ttm->be_list, sizeof(*ttm->be_list), DRM_MEM_TTM); - ttm->be_list = NULL; + if (be) { + be->destroy(be); + ttm->be = NULL; } if (ttm->pages) { drm_buffer_manager_t *bm = &ttm->dev->bm; - int do_tlbflush = 0; + if (ttm->page_flags & DRM_TTM_PAGE_UNCACHED) + drm_set_caching(ttm, 0); + for (i = 0; i < ttm->num_pages; ++i) { cur_page = ttm->pages + i; - if (ttm->page_flags && - (ttm->page_flags[i] & DRM_TTM_PAGE_UNCACHED) && - *cur_page && !PageHighMem(*cur_page)) { - unmap_page_from_agp(*cur_page); - do_tlbflush = 1; - } if (*cur_page) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) unlock_page(*cur_page); +#else + ClearPageReserved(*cur_page); +#endif if (page_count(*cur_page) != 1) { DRM_ERROR("Erroneous page count. " "Leaking pages.\n"); @@ -151,47 +165,66 @@ int drm_destroy_ttm(drm_ttm_t * ttm) --bm->cur_pages; } } - if (do_tlbflush) - flush_agp_mappings(); ttm_free(ttm->pages, ttm->num_pages*sizeof(*ttm->pages), DRM_MEM_TTM); ttm->pages = NULL; } - if (ttm->page_flags) { - ttm_free(ttm->page_flags, ttm->num_pages*sizeof(*ttm->page_flags), - DRM_MEM_TTM); - ttm->page_flags = NULL; - } - - if (ttm->vma_list) { - list_for_each_safe(list, next, &ttm->vma_list->head) { - drm_ttm_vma_list_t *entry = - 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_TTM); - } - drm_free(ttm->vma_list, sizeof(*ttm->vma_list), DRM_MEM_TTM); - ttm->vma_list = NULL; - } - drm_free(ttm, sizeof(*ttm), DRM_MEM_TTM); return 0; } +static int drm_ttm_populate(drm_ttm_t *ttm) +{ + struct page *page; + unsigned long i; + drm_buffer_manager_t *bm; + drm_ttm_backend_t *be; + + + if (ttm->state != ttm_unpopulated) + return 0; + + bm = &ttm->dev->bm; + be = ttm->be; + for (i=0; i<ttm->num_pages; ++i) { + page = ttm->pages[i]; + if (!page) { + if (bm->cur_pages >= bm->max_pages) { + DRM_ERROR("Maximum locked page count exceeded\n"); + return -ENOMEM; + } + page = drm_alloc_gatt_pages(0); + if (!page) + return -ENOMEM; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) + SetPageLocked(page); +#else + SetPageReserved(page); +#endif + ttm->pages[i] = page; + ++bm->cur_pages; + } + } + be->populate(be, ttm->num_pages, ttm->pages); + ttm->state = ttm_unbound; + return 0; +} + + + /* * Initialize a ttm. - * FIXME: Avoid using vmalloc for the page- and page_flags tables? */ -static drm_ttm_t *drm_init_ttm(struct drm_device *dev, unsigned long size) +static drm_ttm_t *drm_init_ttm(struct drm_device *dev, unsigned long size, + int cached) { - + drm_bo_driver_t *bo_driver = dev->driver->bo_driver; drm_ttm_t *ttm; - if (!dev->driver->bo_driver) + if (!bo_driver) return NULL; ttm = drm_calloc(1, sizeof(*ttm), DRM_MEM_TTM); @@ -199,21 +232,12 @@ static drm_ttm_t *drm_init_ttm(struct drm_device *dev, unsigned long size) return NULL; ttm->dev = dev; - ttm->lhandle = 0; atomic_set(&ttm->vma_count, 0); ttm->destroy = 0; ttm->num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; - ttm->page_flags = ttm_alloc(ttm->num_pages * sizeof(*ttm->page_flags), - DRM_MEM_TTM); - if (!ttm->page_flags) { - drm_destroy_ttm(ttm); - DRM_ERROR("Failed allocating page_flags table\n"); - return NULL; - } - memset(ttm->page_flags, 0, ttm->num_pages * sizeof(*ttm->page_flags)); - + ttm->page_flags = 0; ttm->pages = ttm_alloc(ttm->num_pages * sizeof(*ttm->pages), DRM_MEM_TTM); if (!ttm->pages) { @@ -222,382 +246,86 @@ static drm_ttm_t *drm_init_ttm(struct drm_device *dev, unsigned long size) return NULL; } memset(ttm->pages, 0, ttm->num_pages * sizeof(*ttm->pages)); - - 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); - return NULL; - } - - INIT_LIST_HEAD(&ttm->be_list->head); - INIT_LIST_HEAD(&ttm->p_mm_list); - atomic_set(&ttm->shared_count, 0); - ttm->mm_list_seq = 0; - - ttm->vma_list = drm_calloc(1, sizeof(*ttm->vma_list), DRM_MEM_TTM); - if (!ttm->vma_list) { - DRM_ERROR("Alloc vma list failed\n"); + ttm->be = bo_driver->create_ttm_backend_entry(dev, cached); + if (!ttm->be) { drm_destroy_ttm(ttm); + DRM_ERROR("Failed creating ttm backend entry\n"); return NULL; } - - INIT_LIST_HEAD(&ttm->vma_list->head); - - ttm->lhandle = (unsigned long)ttm; - + ttm->state = ttm_unpopulated; return ttm; } /* - * Change caching policy for the linear kernel map - * for range of pages in a ttm. + * Unbind a ttm region from the aperture. */ -static int drm_set_caching(drm_ttm_t * ttm, unsigned long page_offset, - unsigned long num_pages, int noncached) +int drm_evict_ttm(drm_ttm_t * ttm) { - int i, cur; - struct page **cur_page; - int do_tlbflush = 0; + drm_ttm_backend_t *be = ttm->be; - for (i = 0; i < num_pages; ++i) { - cur = page_offset + i; - cur_page = ttm->pages + cur; - if (*cur_page) { - if (PageHighMem(*cur_page)) { - if (noncached - && page_address(*cur_page) != NULL) { - DRM_ERROR - ("Illegal mapped HighMem Page\n"); - return -EINVAL; - } - } else if ((ttm->page_flags[cur] & - DRM_TTM_PAGE_UNCACHED) != noncached) { - DRM_MASK_VAL(ttm->page_flags[cur], - DRM_TTM_PAGE_UNCACHED, noncached); - if (noncached) { - map_page_into_agp(*cur_page); - } else { - unmap_page_from_agp(*cur_page); - } - do_tlbflush = 1; - } + switch (ttm->state) { + case ttm_bound: + if (be->needs_cache_adjust(be)) { + unmap_vma_pages(ttm); } + be->unbind(be); + break; + default: + break; } - if (do_tlbflush) - flush_agp_mappings(); + ttm->state = ttm_evicted; return 0; } -/* - * Unbind a ttm region from the aperture. - */ - -int drm_evict_ttm_region(drm_ttm_backend_list_t * entry) +void drm_fixup_ttm_caching(drm_ttm_t * ttm) { - drm_ttm_backend_t *be = entry->be; - drm_ttm_t *ttm = entry->owner; - if (be) { - switch (entry->state) { - case ttm_bound: - if (ttm && be->needs_cache_adjust(be)) { - unmap_vma_pages(ttm, entry->page_offset, - entry->num_pages); - } - be->unbind(entry->be); - if (ttm && be->needs_cache_adjust(be)) { - drm_set_caching(ttm, entry->page_offset, - entry->num_pages, 0); - } - break; - default: - break; + if (ttm->state == ttm_evicted) { + drm_ttm_backend_t *be = ttm->be; + if (be->needs_cache_adjust(be)) { + drm_set_caching(ttm, 0); } + ttm->state = ttm_unbound; } - entry->state = ttm_evicted; - return 0; } + -void drm_unbind_ttm_region(drm_ttm_backend_list_t * entry) +void drm_unbind_ttm(drm_ttm_t * ttm) { - drm_evict_ttm_region(entry); - entry->state = ttm_unbound; -} - -/* - * Destroy and clean up all resources associated with a ttm region. - * FIXME: release pages to OS when doing this operation. - */ - -void drm_destroy_ttm_region(drm_ttm_backend_list_t * entry) -{ - drm_ttm_backend_t *be = entry->be; - drm_ttm_t *ttm = entry->owner; - uint32_t *cur_page_flags; - int i; - - DRM_DEBUG("Destroying a TTM region\n"); - list_del_init(&entry->head); + if (ttm->state == ttm_bound) + drm_evict_ttm(ttm); - drm_unbind_ttm_region(entry); - if (be) { - be->clear(be); - be->destroy(be); - } - cur_page_flags = ttm->page_flags + entry->page_offset; - for (i = 0; i < entry->num_pages; ++i) { - DRM_MASK_VAL(*cur_page_flags, DRM_TTM_PAGE_USED, 0); - cur_page_flags++; - } - - drm_free(entry, sizeof(*entry), DRM_MEM_TTM); + drm_fixup_ttm_caching(ttm); } -/* - * Create a ttm region from a range of ttm pages. - */ - -int drm_create_ttm_region(drm_ttm_t * ttm, unsigned long page_offset, - unsigned long n_pages, int cached, - drm_ttm_backend_list_t ** region) +int drm_bind_ttm(drm_ttm_t * ttm, + unsigned long aper_offset) { - struct page **cur_page; - uint32_t *cur_page_flags; - drm_ttm_backend_list_t *entry; - drm_ttm_backend_t *be; - int ret, i; - drm_buffer_manager_t *bm = &ttm->dev->bm; - if ((page_offset + n_pages) > ttm->num_pages || n_pages == 0) { - DRM_ERROR("Region Doesn't fit ttm\n"); - return -EINVAL; - } - - cur_page_flags = ttm->page_flags + page_offset; - for (i = 0; i < n_pages; ++i, ++cur_page_flags) { - if (*cur_page_flags & DRM_TTM_PAGE_USED) { - DRM_ERROR("TTM region overlap\n"); - return -EINVAL; - } else { - DRM_MASK_VAL(*cur_page_flags, DRM_TTM_PAGE_USED, - DRM_TTM_PAGE_USED); - } - } - - 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_TTM); - DRM_ERROR("Couldn't create backend.\n"); - return -EINVAL; - } - entry->state = ttm_unbound; - entry->page_offset = page_offset; - entry->num_pages = n_pages; - entry->be = be; - entry->owner = ttm; - - INIT_LIST_HEAD(&entry->head); - list_add_tail(&entry->head, &ttm->be_list->head); - - for (i = 0; i < entry->num_pages; ++i) { - cur_page = ttm->pages + (page_offset + i); - if (!*cur_page) { - if (bm->cur_pages >= bm->max_pages) { - DRM_ERROR("Maximum locked page count exceeded\n"); - drm_destroy_ttm_region(entry); - return -ENOMEM; - } - *cur_page = drm_alloc_gatt_pages(0); - if (!*cur_page) { - DRM_ERROR("Page allocation failed\n"); - drm_destroy_ttm_region(entry); - return -ENOMEM; - } - SetPageLocked(*cur_page); - ++bm->cur_pages; - } - } - - if ((ret = be->populate(be, n_pages, ttm->pages + page_offset))) { - drm_destroy_ttm_region(entry); - DRM_ERROR("Couldn't populate backend.\n"); - return ret; - } - ttm->aperture_base = be->aperture_base; - - *region = entry; - return 0; -} - -/* - * Bind a ttm region. Set correct caching policy. - */ - -int drm_bind_ttm_region(drm_ttm_backend_list_t * region, - unsigned long aper_offset) -{ - - int i; - uint32_t *cur_page_flag; int ret = 0; drm_ttm_backend_t *be; - drm_ttm_t *ttm; - if (!region || region->state == ttm_bound) + if (!ttm) return -EINVAL; + if (ttm->state == ttm_bound) + return 0; - be = region->be; - ttm = region->owner; - - if (ttm && be->needs_cache_adjust(be)) { - if (ret) - return ret; - - unmap_vma_pages(ttm, region->page_offset, - region->num_pages); - drm_set_caching(ttm, region->page_offset, region->num_pages, - DRM_TTM_PAGE_UNCACHED); - } else { - DRM_DEBUG("Binding cached\n"); - } - + be = ttm->be; + + drm_ttm_populate(ttm); + if (ttm->state == ttm_unbound && be->needs_cache_adjust(be)) { + unmap_vma_pages(ttm); + drm_set_caching(ttm, DRM_TTM_PAGE_UNCACHED); + } if ((ret = be->bind(be, aper_offset))) { - drm_unbind_ttm_region(region); + drm_unbind_ttm(ttm); DRM_ERROR("Couldn't bind backend.\n"); return ret; } - cur_page_flag = ttm->page_flags + region->page_offset; - for (i = 0; i < region->num_pages; ++i) { - DRM_MASK_VAL(*cur_page_flag, DRM_TTM_MASK_PFN, - (i + aper_offset) << PAGE_SHIFT); - cur_page_flag++; - } - - region->state = ttm_bound; - return 0; -} - -int drm_rebind_ttm_region(drm_ttm_backend_list_t * entry, - unsigned long aper_offset) -{ - return drm_bind_ttm_region(entry, aper_offset); - -} - -/* - * Destroy an anonymous ttm region. - */ - -void drm_user_destroy_region(drm_ttm_backend_list_t * entry) -{ - drm_ttm_backend_t *be; - struct page **cur_page; - int i; - - if (!entry || entry->owner) - return; - - be = entry->be; - if (!be) { - drm_free(entry, sizeof(*entry), DRM_MEM_TTM); - return; - } - - be->unbind(be); - - if (entry->anon_pages) { - cur_page = entry->anon_pages; - for (i = 0; i < entry->anon_locked; ++i) { - if (!PageReserved(*cur_page)) - SetPageDirty(*cur_page); - page_cache_release(*cur_page); - cur_page++; - } - ttm_free(entry->anon_pages, - sizeof(*entry->anon_pages)*entry->anon_locked, - DRM_MEM_TTM); - } - - be->destroy(be); - drm_free(entry, sizeof(*entry), DRM_MEM_TTM); - return; -} - -/* - * Create a ttm region from an arbitrary region of user pages. - * Since this region has no backing ttm, it's owner is set to - * null, and it is registered with the file of the caller. - * Gets destroyed when the file is closed. We call this an - * anonymous ttm region. - */ - -int drm_user_create_region(drm_device_t * dev, unsigned long start, int len, - drm_ttm_backend_list_t ** entry) -{ - drm_ttm_backend_list_t *tmp; - drm_ttm_backend_t *be; - int ret; - - if (len <= 0) - return -EINVAL; - if (!dev->driver->bo_driver->create_ttm_backend_entry) - return -EFAULT; - - tmp = drm_calloc(1, sizeof(*tmp), DRM_MEM_TTM); - - if (!tmp) - return -ENOMEM; - - be = dev->driver->bo_driver->create_ttm_backend_entry(dev, 1); - tmp->be = be; - - if (!be) { - drm_user_destroy_region(tmp); - return -ENOMEM; - } - if (be->needs_cache_adjust(be)) { - drm_user_destroy_region(tmp); - return -EFAULT; - } - - tmp->anon_pages = ttm_alloc(sizeof(*(tmp->anon_pages)) * len, - DRM_MEM_TTM); - - if (!tmp->anon_pages) { - drm_user_destroy_region(tmp); - return -ENOMEM; - } - - down_read(¤t->mm->mmap_sem); - ret = get_user_pages(current, current->mm, start, len, 1, 0, - tmp->anon_pages, NULL); - up_read(¤t->mm->mmap_sem); - - if (ret != len) { - drm_user_destroy_region(tmp); - DRM_ERROR("Could not lock %d pages. Return code was %d\n", - len, ret); - return -EPERM; - } - tmp->anon_locked = len; - - ret = be->populate(be, len, tmp->anon_pages); - - if (ret) { - drm_user_destroy_region(tmp); - return ret; - } - - tmp->state = ttm_unbound; - *entry = tmp; + ttm->aper_offset = aper_offset; + ttm->state = ttm_bound; return 0; } @@ -652,28 +380,17 @@ void drm_ttm_object_deref_unlocked(drm_device_t * dev, drm_ttm_object_t * to) } /* - * dev->struct_mutex locked. - */ -static void drm_ttm_user_deref_locked(drm_file_t * priv, - drm_user_object_t * base) -{ - drm_ttm_object_deref_locked(priv->head->dev, - drm_user_object_entry(base, - drm_ttm_object_t, - base)); -} - -/* * Create a ttm and add it to the drm book-keeping. * dev->struct_mutex locked. */ int drm_ttm_object_create(drm_device_t * dev, unsigned long size, - uint32_t flags, drm_ttm_object_t ** ttm_object) + uint32_t flags, int cached, + drm_ttm_object_t ** ttm_object) { drm_ttm_object_t *object; drm_map_list_t *list; - drm_map_t *map; + drm_local_map_t *map; drm_ttm_t *ttm; object = drm_calloc(1, sizeof(*object), DRM_MEM_TTM); @@ -689,14 +406,14 @@ int drm_ttm_object_create(drm_device_t * dev, unsigned long size, } map = list->map; - ttm = drm_init_ttm(dev, size); + ttm = drm_init_ttm(dev, size, cached); if (!ttm) { DRM_ERROR("Could not create ttm\n"); drm_ttm_object_remove(dev, object); return -ENOMEM; } - map->offset = ttm->lhandle; + map->offset = (unsigned long) ttm; map->type = _DRM_TTM; map->flags = _DRM_REMOVABLE; map->size = ttm->num_pages * PAGE_SIZE; @@ -725,87 +442,3 @@ int drm_ttm_object_create(drm_device_t * dev, unsigned long size, *ttm_object = object; return 0; } - -drm_ttm_object_t *drm_lookup_ttm_object(drm_file_t * priv, uint32_t handle, - int check_owner) -{ - drm_user_object_t *uo; - drm_ttm_object_t *to; - - uo = drm_lookup_user_object(priv, handle); - - if (!uo || (uo->type != drm_ttm_type)) - return NULL; - - if (check_owner && priv != uo->owner) { - if (!drm_lookup_ref_object(priv, uo, _DRM_REF_USE)) - return NULL; - } - - to = drm_user_object_entry(uo, drm_ttm_object_t, base); - atomic_inc(&to->usage); - return to; -} - -int drm_ttm_ioctl(DRM_IOCTL_ARGS) -{ - DRM_DEVICE; - drm_ttm_arg_t arg; - drm_ttm_object_t *entry; - drm_user_object_t *uo; - unsigned long size; - int ret; - - DRM_COPY_FROM_USER_IOCTL(arg, (void __user *)data, sizeof(arg)); - - switch (arg.op) { - case drm_ttm_create: - mutex_lock(&dev->struct_mutex); - size = arg.size; - ret = drm_ttm_object_create(dev, size, arg.flags, &entry); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ret; - } - ret = drm_add_user_object(priv, &entry->base, - arg.flags & DRM_TTM_FLAG_SHAREABLE); - if (ret) { - drm_ttm_object_remove(dev, entry); - mutex_unlock(&dev->struct_mutex); - return ret; - } - entry->base.remove = drm_ttm_user_deref_locked; - entry->base.type = drm_ttm_type; - entry->base.ref_struct_locked = NULL; - entry->base.unref = NULL; - atomic_inc(&entry->usage); - break; - case drm_ttm_reference: - ret = drm_user_object_ref(priv, arg.handle, drm_ttm_type, &uo); - if (ret) - return ret; - mutex_lock(&dev->struct_mutex); - entry = drm_lookup_ttm_object(priv, arg.handle, 0); - break; - case drm_ttm_unreference: - return drm_user_object_unref(priv, arg.handle, drm_ttm_type); - case drm_ttm_destroy: - mutex_lock(&dev->struct_mutex); - uo = drm_lookup_user_object(priv, arg.handle); - if (!uo || (uo->type != drm_ttm_type) || uo->owner != priv) { - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - ret = drm_remove_user_object(priv, uo); - mutex_unlock(&dev->struct_mutex); - return ret; - } - arg.handle = entry->base.hash.key; - arg.user_token = entry->map_list.user_token; - arg.size = entry->map_list.map->size; - drm_ttm_object_deref_locked(dev, entry); - mutex_unlock(&dev->struct_mutex); - - DRM_COPY_TO_USER_IOCTL((void __user *)data, arg, sizeof(arg)); - return 0; -} |