From 5af87acbc2025b9f72d51b30f176e9c3909695ac Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 1 May 2008 14:20:44 -0700 Subject: [PATCH] checkpoint: gtt binding written. --- linux-core/drmP.h | 9 ++- linux-core/drm_agpsupport.c | 47 ++++++++++++ linux-core/drm_gem.c | 5 +- linux-core/i915_gem.c | 170 ++++++++++++++++++++++++++++++++++++++++---- shared-core/i915_drv.h | 14 +++- 5 files changed, 228 insertions(+), 17 deletions(-) diff --git a/linux-core/drmP.h b/linux-core/drmP.h index c582b80..2ed17b8 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 0aa94a7..b37d6d9 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 bd51362..def526f 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 0685a1b..bd030a8 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) diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 7913f48..431fc43 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -256,7 +256,19 @@ enum intel_chip_family { /** driver private structure attached to each drm_gem_object */ struct drm_i915_gem_object { - /** Current offset of the object in GTT space, if any. */ + /** Current space allocated to this object in the GTT, if any. */ + struct drm_memrange_node *gtt_space; + + /** AGP memory structure for our GTT binding. */ + DRM_AGP_MEM *agp_mem; + + struct page **page_list; + + /** + * Current offset of the object in GTT space. + * + * This is the same as gtt_space->start + */ uint32_t gtt_offset; /** Boolean whether this object has a valid gtt offset. */ -- 2.7.4