summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2008-06-20 00:21:37 -0700
committerKeith Packard <keithp@keithp.com>2008-06-20 00:21:57 -0700
commit918420deefb978d4e572121b4273d717bdbfde8e (patch)
tree78ae637bb0ed953d4d3849b09ee89cfe0139b492
parent52e5d24fae4af6f2f4a5304a516c8c5ab347a11b (diff)
[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.
-rw-r--r--linux-core/i915_gem.c44
-rw-r--r--shared-core/i915_drv.h6
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;