Add a bit of /proc/dri/*/gem support. Clean up some refcount/pagelock issues.
authorKeith Packard <keithp@keithp.com>
Fri, 2 May 2008 23:34:16 +0000 (16:34 -0700)
committerKeith Packard <keithp@keithp.com>
Fri, 2 May 2008 23:34:16 +0000 (16:34 -0700)
Track named objects in /proc/dri/0/gem_names.
Track total object count in /proc/dri/0/gem_objects.
Initialize device gem data.
return -ENODEV for gem ioctls if the driver doesn't support gem.
Call unlock_page when unbinding from gtt.
Add numerous misssing calls to drm_gem_object_unreference.

linux-core/drmP.h
linux-core/drm_gem.c
linux-core/drm_proc.c
linux-core/drm_stub.c
linux-core/i915_drv.c
linux-core/i915_gem.c

index ebbbfa8..12e093e 100644 (file)
@@ -937,6 +937,7 @@ struct drm_device {
        /*@{ */
        spinlock_t object_name_lock;
        struct idr object_name_idr;
+       atomic_t object_count;
        /*@} */
 };
 
@@ -1320,6 +1321,9 @@ static inline struct drm_memrange *drm_get_mm(struct drm_memrange_node *block)
        return block->mm;
 }
 
+int
+drm_gem_init (struct drm_device *dev);
+
 void
 drm_gem_object_free (struct kref *kref);
 
index 2e963f2..db12f9a 100644 (file)
  * up at a later data, and as our interface with shmfs for memory allocation.
  */
 
+/**
+ * Initialize the GEM device fields
+ */
+
+int
+drm_gem_init (struct drm_device *dev)
+{
+       spin_lock_init (&dev->object_name_lock);
+       idr_init (&dev->object_name_idr);
+       atomic_set (&dev->object_count, 0);
+       return 0;
+}
+
+/**
+ * Allocate a GEM object of the specified size with shmfs backing store
+ */
 static struct drm_gem_object *
 drm_gem_object_alloc(struct drm_device *dev, size_t size)
 {
@@ -90,6 +106,7 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size)
                kfree(obj);
                return NULL;
        }
+       atomic_inc (&dev->object_count);
        return obj;
 }
 
@@ -145,10 +162,9 @@ drm_gem_handle_create (struct drm_file *file_priv,
         */
 again:
        /* ensure there is space available to allocate a handle */
-       if (idr_pre_get(&file_priv->object_idr, GFP_KERNEL) == 0) {
-               kfree(obj);
+       if (idr_pre_get(&file_priv->object_idr, GFP_KERNEL) == 0)
                return -ENOMEM;
-       }
+
        /* do the allocation under our spinlock */
        spin_lock (&file_priv->table_lock);
        ret = idr_get_new_above(&file_priv->object_idr, obj, 1, handlep);
@@ -198,6 +214,9 @@ drm_gem_alloc_ioctl(struct drm_device *dev, void *data,
        struct drm_gem_object *obj;
        int handle, ret;
 
+       if (!(dev->driver->driver_features & DRIVER_GEM))
+               return -ENODEV;
+
        /* Round requested size up to page size */
        args->size = (args->size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
 
@@ -227,6 +246,9 @@ drm_gem_unreference_ioctl(struct drm_device *dev, void *data,
        struct drm_gem_unreference *args = data;
        int ret;
 
+       if (!(dev->driver->driver_features & DRIVER_GEM))
+               return -ENODEV;
+
        ret = drm_gem_handle_delete(file_priv, args->handle);
 
        return ret;
@@ -246,6 +268,9 @@ drm_gem_pread_ioctl(struct drm_device *dev, void *data,
        ssize_t read;
        loff_t offset;
 
+       if (!(dev->driver->driver_features & DRIVER_GEM))
+               return -ENODEV;
+
        obj = drm_gem_object_lookup(dev, file_priv, args->handle);
        if (obj == NULL)
                return -EINVAL;
@@ -282,6 +307,9 @@ drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
        struct drm_gem_object *obj;
        loff_t offset;
 
+       if (!(dev->driver->driver_features & DRIVER_GEM))
+               return -ENODEV;
+
        obj = drm_gem_object_lookup(dev, file_priv, args->handle);
        if (obj == NULL)
                return -EINVAL;
@@ -313,6 +341,9 @@ drm_gem_pwrite_ioctl(struct drm_device *dev, void *data,
        ssize_t written;
        loff_t offset;
 
+       if (!(dev->driver->driver_features & DRIVER_GEM))
+               return -ENODEV;
+
        obj = drm_gem_object_lookup(dev, file_priv, args->handle);
        if (obj == NULL)
                return -EINVAL;
@@ -348,6 +379,9 @@ drm_gem_name_ioctl(struct drm_device *dev, void *data,
        struct drm_gem_object *obj;
        int ret;
 
+       if (!(dev->driver->driver_features & DRIVER_GEM))
+               return -ENODEV;
+
        obj = drm_gem_object_lookup(dev, file_priv, args->handle);
        if (obj == NULL)
                return -EINVAL;
@@ -396,6 +430,9 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data,
        int ret;
        int handle;
 
+       if (!(dev->driver->driver_features & DRIVER_GEM))
+               return -ENODEV;
+
        spin_lock (&dev->object_name_lock);
        obj = idr_find (&dev->object_name_idr, (int) args->name);
        if (obj)
@@ -423,6 +460,7 @@ void
 drm_gem_open(struct drm_device *dev, struct drm_file *file_private)
 {
        idr_init(&file_private->object_idr);
+       spin_lock_init (&file_private->table_lock);
 }
 
 /** Called at device close to release the file's handle references on objects. */
@@ -464,7 +502,7 @@ drm_gem_object_free (struct kref *kref)
                dev->driver->gem_free_object(obj);
 
        fput(obj->filp);
-
+       atomic_dec (&dev->object_count);
        kfree(obj);
 }
 EXPORT_SYMBOL(drm_gem_object_free);
index 42da5c6..b1b976b 100644 (file)
@@ -51,6 +51,10 @@ static int drm_bufs_info(char *buf, char **start, off_t offset,
                         int request, int *eof, void *data);
 static int drm_objects_info(char *buf, char **start, off_t offset,
                         int request, int *eof, void *data);
+static int drm_gem_name_info(char *buf, char **start, off_t offset,
+                            int request, int *eof, void *data);
+static int drm_gem_object_info(char *buf, char **start, off_t offset,
+                              int request, int *eof, void *data);
 #if DRM_DEBUG_CODE
 static int drm_vma_info(char *buf, char **start, off_t offset,
                        int request, int *eof, void *data);
@@ -70,6 +74,8 @@ static struct drm_proc_list {
        {"queues", drm_queues_info},
        {"bufs", drm_bufs_info},
        {"objects", drm_objects_info},
+       {"gem_names", drm_gem_name_info},
+       {"gem_objects", drm_gem_object_info},
 #if DRM_DEBUG_CODE
        {"vma", drm_vma_info},
 #endif
@@ -582,6 +588,79 @@ static int drm_clients_info(char *buf, char **start, off_t offset,
        return ret;
 }
 
+struct drm_gem_name_info_data {
+       int                     len;
+       char                    *buf;
+       int                     eof;
+};
+
+static int drm_gem_one_name_info (int id, void *ptr, void *data)
+{
+       struct drm_gem_object *obj = ptr;
+       struct drm_gem_name_info_data   *nid = data;
+
+       DRM_INFO ("name %d size %d\n", obj->name, obj->size);
+       if (nid->eof)
+               return 0;
+       
+       nid->len += sprintf (&nid->buf[nid->len],
+                            "%6d%9d%8d%9d\n",
+                            obj->name, obj->size,
+                            atomic_read(&obj->handlecount.refcount),
+                            atomic_read(&obj->refcount.refcount));
+       if (nid->len > DRM_PROC_LIMIT) {
+               nid->eof = 1;
+               return 0;
+       }
+       return 0;
+}
+
+static int drm_gem_name_info(char *buf, char **start, off_t offset,
+                            int request, int *eof, void *data)
+{
+       struct drm_minor *minor = (struct drm_minor *) data; 
+       struct drm_device *dev = minor->dev;
+       struct drm_gem_name_info_data nid;
+       
+       if (offset > DRM_PROC_LIMIT) {
+               *eof = 1;
+               return 0;
+       }
+
+       nid.len = sprintf (buf, "  name     size handles refcount\n");
+       nid.buf = buf;
+       nid.eof = 0;
+       idr_for_each (&dev->object_name_idr, drm_gem_one_name_info, &nid);
+       
+       *start = &buf[offset];
+       *eof = 0;
+       if (nid.len > request + offset)
+               return request;
+       *eof = 1;
+       return nid.len - offset;
+}
+
+static int drm_gem_object_info(char *buf, char **start, off_t offset,
+                              int request, int *eof, void *data)
+{
+       struct drm_minor *minor = (struct drm_minor *) data; 
+       struct drm_device *dev = minor->dev;
+       int len = 0;
+       
+       if (offset > DRM_PROC_LIMIT) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = &buf[offset];
+       *eof = 0;
+       DRM_PROC_PRINT ("%d objects\n", atomic_read (&dev->object_count));
+       if (len > request + offset)
+               return request;
+       *eof = 1;
+       return len - offset;
+}
+
 #if DRM_DEBUG_CODE
 
 static int drm__vma_info(char *buf, char **start, off_t offset, int request,
index 030fea5..5584182 100644 (file)
@@ -163,7 +163,16 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
                goto error_out_unreg;
        }
 
+       if (driver->driver_features & DRIVER_GEM) {
+               retcode = drm_gem_init (dev);
+               if (retcode) {
+                       DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n");
+                       goto error_out_unreg;
+               }
+       }
+
        drm_fence_manager_init(dev);
+
        return 0;
 
 error_out_unreg:
index c77c329..3e788d2 100644 (file)
@@ -566,7 +566,7 @@ static struct drm_driver driver = {
         */
        .driver_features =
            DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR | */
-           DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
+           DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM,
        .load = i915_driver_load,
        .unload = i915_driver_unload,
        .firstopen = i915_driver_firstopen,
index 2ac74b4..747cc45 100644 (file)
@@ -58,11 +58,13 @@ i915_gem_object_free_page_list(struct drm_gem_object *obj)
        if (obj_priv->page_list == NULL)
                return;
 
-       for (i = 0; i < obj->size / PAGE_SIZE; i++) {
-               if (obj_priv->page_list[i] == NULL)
-                       put_page(obj_priv->page_list[i]);
+       
+       for (i = 0; i < page_count; i++) {
+               if (obj_priv->page_list[i] != NULL) {
+                       unlock_page (obj_priv->page_list[i]);
+                       page_cache_release (obj_priv->page_list[i]);
+               }
        }
-
        drm_free(obj_priv->page_list,
                 page_count * sizeof(struct page *),
                 DRM_MEM_DRIVER);
@@ -212,15 +214,18 @@ i915_gem_reloc_and_validate_object(struct drm_gem_object *obj,
                if (target_obj_priv->gtt_space == NULL) {
                        DRM_ERROR("No GTT space found for object %d\n",
                                  reloc.target_handle);
+                       drm_gem_object_unreference (target_obj);
                        return -EINVAL;
                }
 
                if (reloc.offset > obj->size - 4) {
                        DRM_ERROR("Relocation beyond object bounds.\n");
+                       drm_gem_object_unreference (target_obj);
                        return -EINVAL;
                }
                if (reloc.offset & 3) {
                        DRM_ERROR("Relocation not 4-byte aligned.\n");
+                       drm_gem_object_unreference (target_obj);
                        return -EINVAL;
                }
 
@@ -231,7 +236,10 @@ i915_gem_reloc_and_validate_object(struct drm_gem_object *obj,
                                     (reloc.offset & ~(PAGE_SIZE - 1)),
                                     PAGE_SIZE);
                if (reloc_page == NULL)
+               {
+                       drm_gem_object_unreference (target_obj);
                        return -ENOMEM;
+               }
 
                reloc_entry = (uint32_t *)((char *)reloc_page +
                                           (reloc.offset & (PAGE_SIZE - 1)));
@@ -242,6 +250,7 @@ i915_gem_reloc_and_validate_object(struct drm_gem_object *obj,
                *reloc_entry = reloc_val;
 
                iounmap(reloc_page);
+               drm_gem_object_unreference (target_obj);
        }
 
        return 0;
@@ -372,16 +381,21 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
                return -EINVAL;
        }
 
-       ret = i915_gem_object_bind_to_gtt(obj, (unsigned) args->alignment);
-       if (ret != 0) {
-               DRM_ERROR("Failure to bind in i915_gem_pin_ioctl(): %d\n",
-                         ret);
-               return ret;
+       obj_priv = obj->driver_private;
+       if (obj_priv->gtt_space == NULL)
+       {
+               ret = i915_gem_object_bind_to_gtt(obj, (unsigned) args->alignment);
+               if (ret != 0) {
+                       DRM_ERROR("Failure to bind in i915_gem_pin_ioctl(): %d\n",
+                                 ret);
+                       drm_gem_object_unreference (obj);
+                       return ret;
+               }
        }
 
-       obj_priv = obj->driver_private;
        obj_priv->pin_count++;
        args->offset = obj_priv->gtt_offset;
+       drm_gem_object_unreference (obj);
 
        return 0;
 }
@@ -403,7 +417,7 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
 
        obj_priv = obj->driver_private;
        obj_priv->pin_count--;
-
+       drm_gem_object_unreference (obj);
        return 0;
 }