summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_gem.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_gem.c')
-rw-r--r--drivers/gpu/drm/drm_gem.c139
1 files changed, 75 insertions, 64 deletions
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index b9046089e9e..dddb373af96 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -223,27 +223,25 @@ drm_gem_handle_delete(struct drm_file *filp, u32 handle)
struct drm_device *dev;
struct drm_gem_object *obj;
- /* This is gross. The idr system doesn't let us try a delete and
- * return an error code. It just spews if you fail at deleting.
- * So, we have to grab a lock around finding the object and then
- * doing the delete on it and dropping the refcount, or the user
- * could race us to double-decrement the refcount and cause a
- * use-after-free later. Given the frequency of our handle lookups,
- * we may want to use ida for number allocation and a hash table
- * for the pointers, anyway.
- */
mutex_lock(&filp->table_lock);
- /* Check if we currently have a reference on the object */
- obj = idr_find(&filp->object_idr, handle);
- if (obj == NULL) {
+ if (handle > filp->object_table_size) {
mutex_unlock(&filp->table_lock);
return -EINVAL;
}
+
+ obj = filp->object_table[handle];
+ if (!obj) {
+ mutex_unlock(&filp->table_lock);
+ return -EINVAL;
+ }
+
dev = obj->dev;
/* Release reference and decrement refcount. */
- idr_remove(&filp->object_idr, handle);
+ filp->object_table[handle] = NULL;
+ ida_simple_remove(&filp->object_ida, handle);
+
mutex_unlock(&filp->table_lock);
drm_gem_remove_prime_handles(obj, filp);
@@ -267,31 +265,40 @@ drm_gem_handle_create(struct drm_file *file_priv,
u32 *handlep)
{
struct drm_device *dev = obj->dev;
- int ret;
+ int ret, handle;
- /*
- * Get the user-visible handle using idr.
- */
-again:
- /* ensure there is space available to allocate a handle */
- if (idr_pre_get(&file_priv->object_idr, GFP_KERNEL) == 0)
- return -ENOMEM;
+ /* Get the user-visible handle using ida. */
+ ret = ida_simple_get(&file_priv->object_ida, 1, 0, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+ handle = ret;
- /* do the allocation under our spinlock */
mutex_lock(&file_priv->table_lock);
- ret = idr_get_new_above(&file_priv->object_idr, obj, 1, (int *)handlep);
- mutex_unlock(&file_priv->table_lock);
- if (ret == -EAGAIN)
- goto again;
- else if (ret)
- return ret;
+ /* Resize the table of object pointers if necessary. */
+ if (file_priv->object_table_size <= handle) {
+ u32 new_table_size = file_priv->object_table_size * 2;
+ void *new_table = vzalloc(new_table_size * sizeof(void *));
+ if (!new_table) {
+ ida_simple_remove(&file_priv->object_ida, handle);
+ mutex_unlock(&file_priv->table_lock);
+ return -ENOMEM;
+ }
+ memcpy(new_table, file_priv->object_table,
+ file_priv->object_table_size * sizeof(void *));
+ vfree(file_priv->object_table);
+ file_priv->object_table = new_table;
+ file_priv->object_table_size = new_table_size;
+ }
+ file_priv->object_table[handle] = obj;
+ *handlep = handle;
drm_gem_object_handle_reference(obj);
+ mutex_unlock(&file_priv->table_lock);
if (dev->driver->gem_open_object) {
ret = dev->driver->gem_open_object(obj, file_priv);
if (ret) {
- drm_gem_handle_delete(file_priv, *handlep);
+ drm_gem_handle_delete(file_priv, handle);
return ret;
}
}
@@ -388,20 +395,30 @@ out_free_list:
}
EXPORT_SYMBOL(drm_gem_create_mmap_offset);
-/** Returns a reference to the object named by the handle. */
+/**
+ * Returns the pointer to the object named by handle.
+ *
+ * No reference is taken on the object, and the call must have the
+ * table_lock held.
+ */
+struct drm_gem_object *
+drm_gem_object_find(struct drm_file *filp, u32 handle)
+{
+ if (filp->object_table_size > handle)
+ return filp->object_table[handle];
+ else
+ return NULL;
+}
+EXPORT_SYMBOL(drm_gem_object_find);
+
struct drm_gem_object *
drm_gem_object_lookup_locked(struct drm_file *filp, u32 handle)
{
struct drm_gem_object *obj;
- /* Check if we currently have a reference on the object */
- obj = idr_find(&filp->object_idr, handle);
- if (obj == NULL) {
- mutex_unlock(&filp->table_lock);
- return NULL;
- }
-
- drm_gem_object_reference(obj);
+ obj = drm_gem_object_find(filp, handle);
+ if (obj)
+ drm_gem_object_reference(obj);
return obj;
}
@@ -535,29 +552,10 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data,
void
drm_gem_open(struct drm_device *dev, struct drm_file *file_private)
{
- idr_init(&file_private->object_idr);
+ ida_init(&file_private->object_ida);
mutex_init(&file_private->table_lock);
-}
-
-/**
- * Called at device close to release the file's
- * handle references on objects.
- */
-static int
-drm_gem_object_release_handle(int id, void *ptr, void *data)
-{
- struct drm_file *file_priv = data;
- struct drm_gem_object *obj = ptr;
- struct drm_device *dev = obj->dev;
-
- drm_gem_remove_prime_handles(obj, file_priv);
-
- if (dev->driver->gem_close_object)
- dev->driver->gem_close_object(obj, file_priv);
-
- drm_gem_object_handle_unreference_unlocked(obj);
-
- return 0;
+ file_private->object_table_size = 16;
+ file_private->object_table = vzalloc(16 * sizeof(void *));
}
/**
@@ -568,11 +566,24 @@ drm_gem_object_release_handle(int id, void *ptr, void *data)
void
drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
{
- idr_for_each(&file_private->object_idr,
- &drm_gem_object_release_handle, file_private);
+ u32 i;
+
+ for (i = 0; i < file_private->object_table_size; i++) {
+ struct drm_gem_object *obj = file_private->object_table[i];
+
+ if (!obj)
+ continue;
+
+ drm_gem_remove_prime_handles(obj, file_private);
+
+ if (dev->driver->gem_close_object)
+ dev->driver->gem_close_object(obj, file_private);
+
+ drm_gem_object_handle_unreference_unlocked(obj);
+
+ }
- idr_remove_all(&file_private->object_idr);
- idr_destroy(&file_private->object_idr);
+ ida_destroy(&file_private->object_ida);
}
void