From 918420deefb978d4e572121b4273d717bdbfde8e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 20 Jun 2008 00:21:37 -0700 Subject: [PATCH] [intel-gem] Use shmem_getpage instead of find_or_create_page find_or_create_page doesn't quite set up pages correctly; any newly created pages aren't hooked into the shmem object quite right; user space mmaps of those pages end up mapping pages full of zeros which then get written to the real pages inappropriately. This patch requires that the kernel export shmem_getpage. --- linux-core/i915_gem.c | 44 +++++++++++++++++++++++++++++++++--------- shared-core/i915_drv.h | 6 ++++++ 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index bd4aeaa7..129c9f3e 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -320,8 +320,13 @@ i915_gem_object_free_page_list(struct drm_gem_object *obj) for (i = 0; i < page_count; i++) - if (obj_priv->page_list[i] != NULL) + if (obj_priv->page_list[i] != NULL) { + if (obj_priv->dirty) + set_page_dirty(obj_priv->page_list[i]); + mark_page_accessed(obj_priv->page_list[i]); page_cache_release(obj_priv->page_list[i]); + } + obj_priv->dirty = 0; drm_free(obj_priv->page_list, page_count * sizeof(struct page *), @@ -969,6 +974,11 @@ i915_gem_object_get_page_list(struct drm_gem_object *obj) { struct drm_i915_gem_object *obj_priv = obj->driver_private; int page_count, i; + struct address_space *mapping; + struct inode *inode; + struct page *page; + int ret; + if (obj_priv->page_list) return 0; @@ -984,16 +994,25 @@ i915_gem_object_get_page_list(struct drm_gem_object *obj) return -ENOMEM; } + inode = obj->filp->f_path.dentry->d_inode; + mapping = inode->i_mapping; for (i = 0; i < page_count; i++) { - obj_priv->page_list[i] = - find_or_create_page(obj->filp->f_mapping, i, GFP_HIGHUSER); - - if (obj_priv->page_list[i] == NULL) { - DRM_ERROR("Failed to find_or_create_page()\n"); - i915_gem_object_free_page_list(obj); - return -ENOMEM; + page = find_get_page(mapping, i); + if (page == NULL || !PageUptodate(page)) { + if (page) { + page_cache_release(page); + page = NULL; + } + ret = shmem_getpage(inode, i, &page, SGP_DIRTY, NULL); + + if (ret) { + DRM_ERROR("shmem_getpage failed: %d\n", ret); + i915_gem_object_free_page_list(obj); + return ret; + } + unlock_page(page); } - unlock_page(obj_priv->page_list[i]); + obj_priv->page_list[i] = page; } return 0; } @@ -1239,6 +1258,8 @@ i915_gem_object_set_domain(struct drm_gem_object *obj, */ if (write_domain == 0) read_domains |= obj->read_domains; + else + obj_priv->dirty = 1; /* * Flush the current write domain if @@ -2046,6 +2067,11 @@ int i915_gem_init_object(struct drm_gem_object *obj) void i915_gem_free_object(struct drm_gem_object *obj) { + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + while (obj_priv->pin_count > 0) + i915_gem_object_unpin(obj); + i915_gem_object_unbind(obj); drm_free(obj->driver_private, 1, DRM_MEM_DRIVER); diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 4d8a40d4..94e80cda 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -332,6 +332,12 @@ struct drm_i915_gem_object { */ int active; + /** + * This is set if the object has been written to since last bound + * to the GTT + */ + int dirty; + /** AGP memory structure for our GTT binding. */ DRM_AGP_MEM *agp_mem;