OSDN Git Service

[intel-gem] Use shmem_getpage instead of find_or_create_page
authorKeith Packard <keithp@keithp.com>
Fri, 20 Jun 2008 07:21:37 +0000 (00:21 -0700)
committerKeith Packard <keithp@keithp.com>
Fri, 20 Jun 2008 07:21:57 +0000 (00:21 -0700)
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
shared-core/i915_drv.h

index bd4aeaa..129c9f3 100644 (file)
@@ -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);
index 4d8a40d..94e80cd 100644 (file)
@@ -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;