summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2013-01-08 14:29:58 -0800
committerEric Anholt <eric@anholt.net>2013-01-08 16:00:42 -0800
commitaf3f46c2c9e0604d5459423b60b7e7455b61a29c (patch)
treee41f6c5bcd186331001bbb88d4903337eb60388e
parentd0a69ef2f71306dd64a31cfe6d10b335b0a5d83f (diff)
drm/i915: Use an ida instead of an idr, and maintain our own pointer table.
The multi-layer nature of the idr is too expensive for our purposes, so just use the ida code for number allocation (should be faster than walking our array, since it's a bitmask) Signed-off-by: Eric Anholt <eric@anholt.net>
-rw-r--r--drivers/gpu/drm/drm_gem.c139
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c2
-rw-r--r--include/drm/drmP.h10
3 files changed, 84 insertions, 67 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
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 23a8a51a7ea..a2245be7d82 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -54,7 +54,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
/* Note that objects can't disappear from the idr while we
* hold the struct_mutex, and thus can't be unreferenced.
*/
- target_obj = idr_find(&file->object_idr, reloc->target_handle);
+ target_obj = drm_gem_object_find(file, reloc->target_handle);
if (unlikely(target_obj == NULL))
return -ENOENT;
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 96f582d840d..4d93bb4aee2 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -435,9 +435,12 @@ struct drm_file {
struct drm_minor *minor;
unsigned long lock_count;
+ /** ida used to choose an object handle. */
+ struct ida object_ida;
/** Mapping of mm object handles to object pointers. */
- struct idr object_idr;
- /** Lock for synchronization of access to object_idr. */
+ struct drm_gem_object **object_table;
+ u32 object_table_size;
+ /** Lock for synchronization of access to object_table/object_ida. */
struct mutex table_lock;
struct file *filp;
@@ -1701,6 +1704,9 @@ struct drm_gem_object *drm_gem_object_lookup(struct drm_device *dev,
u32 handle);
struct drm_gem_object *drm_gem_object_lookup_locked(struct drm_file *filp,
u32 handle);
+struct drm_gem_object *drm_gem_object_find(struct drm_file *filp,
+ u32 handle);
+
int drm_gem_close_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_gem_flink_ioctl(struct drm_device *dev, void *data,