diff options
Diffstat (limited to 'drivers/gpu/drm/drm_gem.c')
-rw-r--r-- | drivers/gpu/drm/drm_gem.c | 139 |
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 |