aboutsummaryrefslogtreecommitdiff
path: root/linux-core
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2008-05-01 14:20:44 -0700
committerEric Anholt <eric@anholt.net>2008-05-01 14:20:44 -0700
commit5af87acbc2025b9f72d51b30f176e9c3909695ac (patch)
tree814f963980772d5e3260b834412598cb2c3df90f /linux-core
parent2140e102f942edf7982cee2a3f00caf234551687 (diff)
checkpoint: gtt binding written.
Diffstat (limited to 'linux-core')
-rw-r--r--linux-core/drmP.h9
-rw-r--r--linux-core/drm_agpsupport.c47
-rw-r--r--linux-core/drm_gem.c5
-rw-r--r--linux-core/i915_gem.c170
4 files changed, 215 insertions, 16 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h
index c582b80b..2ed17b81 100644
--- a/linux-core/drmP.h
+++ b/linux-core/drmP.h
@@ -1038,6 +1038,10 @@ extern void drm_free_pages(unsigned long address, int order, int area);
extern DRM_AGP_MEM *drm_alloc_agp(struct drm_device *dev, int pages, u32 type);
extern int drm_free_agp(DRM_AGP_MEM * handle, int pages);
extern int drm_bind_agp(DRM_AGP_MEM * handle, unsigned int start);
+extern DRM_AGP_MEM *drm_agp_bind_pages(struct drm_device *dev,
+ struct page **pages,
+ unsigned long num_pages,
+ uint32_t gtt_offset);
extern int drm_unbind_agp(DRM_AGP_MEM * handle);
extern void drm_free_memctl(size_t size);
@@ -1301,11 +1305,14 @@ static inline struct drm_memrange *drm_get_mm(struct drm_memrange_node *block)
return block->mm;
}
-/* Memory manager (drm_mm.c) */
+/* Graphics Execution Manager library functions (drm_gem.c) */
void drm_gem_object_reference(struct drm_device *dev,
struct drm_gem_object *obj);
void drm_gem_object_unreference(struct drm_device *dev,
struct drm_gem_object *obj);
+struct drm_gem_object *
+drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp,
+ int handle);
int drm_gem_alloc_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_gem_unreference_ioctl(struct drm_device *dev, void *data,
diff --git a/linux-core/drm_agpsupport.c b/linux-core/drm_agpsupport.c
index 0aa94a75..b37d6d9d 100644
--- a/linux-core/drm_agpsupport.c
+++ b/linux-core/drm_agpsupport.c
@@ -484,6 +484,53 @@ int drm_agp_unbind_memory(DRM_AGP_MEM * handle)
return agp_unbind_memory(handle);
}
+/**
+ * Binds a collection of pages into AGP memory at the given offset, returning
+ * the AGP memory structure containing them.
+ *
+ * No reference is held on the pages during this time -- it is up to the
+ * caller to handle that.
+ */
+DRM_AGP_MEM *
+drm_agp_bind_pages(struct drm_device *dev,
+ struct page **pages,
+ unsigned long num_pages,
+ uint32_t gtt_offset)
+{
+ struct page **cur_page, **last_page = pages + num_pages;
+ DRM_AGP_MEM *mem;
+ int ret;
+
+ DRM_DEBUG("drm_agp_populate_ttm\n");
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,11)
+ mem = drm_agp_allocate_memory(num_pages, AGP_USER_MEMORY);
+#else
+ mem = drm_agp_allocate_memory(dev->agp->bridge, num_pages,
+ AGP_USER_MEMORY);
+#endif
+ if (mem == NULL) {
+ DRM_ERROR("Failed to allocate memory for %ld pages\n",
+ num_pages);
+ return NULL;
+ }
+
+ mem->page_count = 0;
+ for (cur_page = pages; cur_page < last_page; ++cur_page) {
+ struct page *page = *cur_page;
+
+ mem->memory[mem->page_count++] =
+ phys_to_gart(page_to_phys(page));
+ }
+
+ mem->is_flushed = TRUE;
+ ret = drm_agp_bind_memory(mem, gtt_offset);
+ if (ret != 0) {
+ agp_free_memory(mem);
+ return NULL;
+ }
+
+ return mem;
+}
/*
diff --git a/linux-core/drm_gem.c b/linux-core/drm_gem.c
index bd51362e..def526f0 100644
--- a/linux-core/drm_gem.c
+++ b/linux-core/drm_gem.c
@@ -127,7 +127,7 @@ drm_gem_handle_delete(struct drm_device *dev, struct drm_file *filp,
}
/** Returns a reference to the object named by the handle. */
-static struct drm_gem_object *
+struct drm_gem_object *
drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp,
int handle)
{
@@ -358,6 +358,9 @@ drm_gem_object_reference(struct drm_device *dev, struct drm_gem_object *obj)
void
drm_gem_object_unreference(struct drm_device *dev, struct drm_gem_object *obj)
{
+ if (obj == NULL)
+ return;
+
spin_lock(&obj->lock);
obj->refcount--;
spin_unlock(&obj->lock);
diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c
index 0685a1b9..bd030a88 100644
--- a/linux-core/i915_gem.c
+++ b/linux-core/i915_gem.c
@@ -49,9 +49,120 @@ i915_gem_init_ioctl(struct drm_device *dev, void *data,
}
static void
-i915_gem_evict_object(struct drm_device *dev, struct drm_gem_object *obj)
+i915_gem_object_free_page_list(struct drm_device *dev,
+ struct drm_gem_object *obj)
{
-
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ int page_count = obj->size / PAGE_SIZE;
+ int i;
+
+ if (obj_priv->page_list == NULL)
+ return;
+
+ /* Count how many we had successfully allocated, since release_pages()
+ * doesn't like NULLs.
+ */
+ for (i = 0; i < obj->size / PAGE_SIZE; i++) {
+ if (obj_priv->page_list[i] == NULL)
+ break;
+ }
+ release_pages(obj_priv->page_list, i, 0);
+
+ drm_free(obj_priv->page_list,
+ page_count * sizeof(struct page *),
+ DRM_MEM_DRIVER);
+ obj_priv->page_list = NULL;
+}
+
+/**
+ * Unbinds an object from the GTT aperture.
+ */
+static void
+i915_gem_object_unbind(struct drm_device *dev, struct drm_gem_object *obj)
+{
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+
+ if (obj_priv->agp_mem != NULL) {
+ drm_unbind_agp(obj_priv->agp_mem);
+ drm_free_agp(obj_priv->agp_mem, obj->size / PAGE_SIZE);
+ }
+
+ i915_gem_object_free_page_list(dev, obj);
+
+ drm_memrange_put_block(obj_priv->gtt_space);
+}
+
+/**
+ * Finds free space in the GTT aperture and binds the object there.
+ */
+static int
+i915_gem_object_bind_to_gtt(struct drm_device *dev, struct drm_gem_object *obj)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ struct drm_memrange_node *free_space;
+ int page_count, i;
+
+ free_space = drm_memrange_search_free(&dev_priv->mm.gtt_space,
+ obj->size,
+ PAGE_SIZE, 0);
+
+ obj_priv->gtt_space = drm_memrange_get_block(free_space,
+ obj->size,
+ PAGE_SIZE);
+
+ /* Get the list of pages out of our struct file. They'll be pinned
+ * at this point until we release them.
+ */
+ page_count = obj->size / PAGE_SIZE;
+ BUG_ON(obj_priv->page_list != NULL);
+ obj_priv->page_list = drm_calloc(page_count, sizeof(struct page *),
+ DRM_MEM_DRIVER);
+ 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) {
+ i915_gem_object_free_page_list(dev, obj);
+ return -ENOMEM;
+ }
+ }
+
+ /* Create an AGP memory structure pointing at our pages, and bind it
+ * into the GTT.
+ */
+ obj_priv->agp_mem = drm_agp_bind_pages(dev,
+ obj_priv->page_list,
+ page_count,
+ obj_priv->gtt_offset);
+ if (obj_priv->agp_mem == NULL) {
+ i915_gem_object_free_page_list(dev, obj);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int
+i915_gem_reloc_and_validate_object(struct drm_device *dev,
+ struct drm_file *file_priv,
+ struct drm_i915_gem_validate_entry *entry,
+ struct drm_gem_object *obj)
+{
+ struct drm_i915_gem_reloc *relocs;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+
+ /* Walk the list of relocations and perform them if necessary. */
+ /* XXX */
+
+ /* Choose the GTT offset for our buffer and put it there. */
+ if (obj_priv->gtt_space == NULL) {
+ i915_gem_object_bind_to_gtt(dev, obj);
+ if (obj_priv->gtt_space == NULL)
+ return -ENOMEM;
+ }
+
+ return 0;
}
static int
@@ -62,18 +173,19 @@ evict_callback(struct drm_memrange_node *node, void *data)
struct drm_i915_gem_object *obj_priv = obj->driver_private;
if (obj_priv->pin_count == 0)
- i915_gem_evict_object(dev, obj);
+ i915_gem_object_unbind(dev, obj);
return 0;
}
int
i915_gem_execbuffer(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+ struct drm_file *file_priv)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_execbuffer *args = data;
struct drm_i915_gem_validate_entry *validate_list;
+ struct drm_gem_object **object_list;
int ret, i;
RING_LOCALS;
@@ -90,34 +202,64 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
return ret;
/* Evict everything so we have space for sure. */
- drm_memrange_for_each(&dev_priv->mm.gtt_space, evict_callback);
+ drm_memrange_for_each(&dev_priv->mm.gtt_space, evict_callback, dev);
/* Copy in the validate list from userland */
validate_list = drm_calloc(sizeof(*validate_list), args->buffer_count,
DRM_MEM_DRIVER);
+ object_list = drm_calloc(sizeof(*object_list), args->buffer_count,
+ DRM_MEM_DRIVER);
+ if (validate_list == NULL || object_list == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
ret = copy_from_user(validate_list,
(struct drm_i915_relocation_entry __user*)
args->buffers,
sizeof(*validate_list) * args->buffer_count);
- if (ret != 0) {
- drm_free(validate_list,
- sizeof(*validate_list) * args->buffer_count,
- DRM_MEM_DRIVER);
- return ret;
- }
+ if (ret != 0)
+ goto err;
- /* Perform the relocations */
+ /* Look up object handles and perform the relocations */
for (i = 0; i < args->buffer_count; i++) {
- intel_gem_reloc_and_validate_buffer(dev, &validate_list[i]);
+ object_list[i] = drm_gem_object_lookup(dev, file_priv,
+ validate_list[i].buffer_handle);
+ if (object_list[i] == NULL) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ i915_gem_reloc_and_validate_object(dev, file_priv,
+ &validate_list[i],
+ object_list[i]);
}
/* Exec the batchbuffer */
+ /* Copy the new buffer offsets back to the user's validate list. */
+ for (i = 0; i < args->buffer_count; i++) {
+ struct drm_i915_gem_object *obj_priv =
+ object_list[i]->driver_private;
+ validate_list[i].buffer_offset = obj_priv->gtt_offset;
+ }
+ ret = copy_to_user(validate_list,
+ (struct drm_i915_relocation_entry __user*)
+ args->buffers,
+ sizeof(*validate_list) * args->buffer_count);
+
+ /* Clean up and return */
+err:
+ if (object_list != NULL) {
+ for (i = 0; i < args->buffer_count; i++)
+ drm_gem_object_unreference(dev, object_list[i]);
+ }
+ drm_free(object_list, sizeof(*object_list) * args->buffer_count,
+ DRM_MEM_DRIVER);
drm_free(validate_list, sizeof(*validate_list) * args->buffer_count,
DRM_MEM_DRIVER);
- return 0;
+ return ret;
}
int i915_gem_init_object(struct drm_device *dev, struct drm_gem_object *obj)