OSDN Git Service

checkpoint: gtt binding written.
authorEric Anholt <eric@anholt.net>
Thu, 1 May 2008 21:20:44 +0000 (14:20 -0700)
committerEric Anholt <eric@anholt.net>
Thu, 1 May 2008 21:20:44 +0000 (14:20 -0700)
linux-core/drmP.h
linux-core/drm_agpsupport.c
linux-core/drm_gem.c
linux-core/i915_gem.c
shared-core/i915_drv.h

index c582b80..2ed17b8 100644 (file)
@@ -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,
index 0aa94a7..b37d6d9 100644 (file)
@@ -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;
+}
 
 
 /*
index bd51362..def526f 100644 (file)
@@ -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);
index 0685a1b..bd030a8 100644 (file)
@@ -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)
index 7913f48..431fc43 100644 (file)
@@ -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. */