diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c | 267 |
1 files changed, 121 insertions, 146 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c index 92be7f6de197..d472a2c8399f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c @@ -35,92 +35,53 @@ #define AMDGPU_BO_LIST_MAX_PRIORITY 32u #define AMDGPU_BO_LIST_NUM_BUCKETS (AMDGPU_BO_LIST_MAX_PRIORITY + 1) -static int amdgpu_bo_list_set(struct amdgpu_device *adev, - struct drm_file *filp, - struct amdgpu_bo_list *list, - struct drm_amdgpu_bo_list_entry *info, - unsigned num_entries); +static void amdgpu_bo_list_free_rcu(struct rcu_head *rcu) +{ + struct amdgpu_bo_list *list = container_of(rcu, struct amdgpu_bo_list, + rhead); + + kvfree(list); +} -static void amdgpu_bo_list_release_rcu(struct kref *ref) +static void amdgpu_bo_list_free(struct kref *ref) { - unsigned i; struct amdgpu_bo_list *list = container_of(ref, struct amdgpu_bo_list, refcount); + struct amdgpu_bo_list_entry *e; - for (i = 0; i < list->num_entries; ++i) - amdgpu_bo_unref(&list->array[i].robj); + amdgpu_bo_list_for_each_entry(e, list) + amdgpu_bo_unref(&e->robj); - mutex_destroy(&list->lock); - kvfree(list->array); - kfree_rcu(list, rhead); + call_rcu(&list->rhead, amdgpu_bo_list_free_rcu); } -static int amdgpu_bo_list_create(struct amdgpu_device *adev, - struct drm_file *filp, - struct drm_amdgpu_bo_list_entry *info, - unsigned num_entries, - int *id) +int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp, + struct drm_amdgpu_bo_list_entry *info, + unsigned num_entries, struct amdgpu_bo_list **result) { - int r; - struct amdgpu_fpriv *fpriv = filp->driver_priv; + unsigned last_entry = 0, first_userptr = num_entries; + struct amdgpu_bo_list_entry *array; struct amdgpu_bo_list *list; + uint64_t total_size = 0; + size_t size; + unsigned i; + int r; + + if (num_entries > SIZE_MAX / sizeof(struct amdgpu_bo_list_entry)) + return -EINVAL; - list = kzalloc(sizeof(struct amdgpu_bo_list), GFP_KERNEL); + size = sizeof(struct amdgpu_bo_list); + size += num_entries * sizeof(struct amdgpu_bo_list_entry); + list = kvmalloc(size, GFP_KERNEL); if (!list) return -ENOMEM; - /* initialize bo list*/ - mutex_init(&list->lock); kref_init(&list->refcount); - r = amdgpu_bo_list_set(adev, filp, list, info, num_entries); - if (r) { - kfree(list); - return r; - } - - /* idr alloc should be called only after initialization of bo list. */ - mutex_lock(&fpriv->bo_list_lock); - r = idr_alloc(&fpriv->bo_list_handles, list, 1, 0, GFP_KERNEL); - mutex_unlock(&fpriv->bo_list_lock); - if (r < 0) { - amdgpu_bo_list_free(list); - return r; - } - *id = r; - - return 0; -} - -static void amdgpu_bo_list_destroy(struct amdgpu_fpriv *fpriv, int id) -{ - struct amdgpu_bo_list *list; - - mutex_lock(&fpriv->bo_list_lock); - list = idr_remove(&fpriv->bo_list_handles, id); - mutex_unlock(&fpriv->bo_list_lock); - if (list) - kref_put(&list->refcount, amdgpu_bo_list_release_rcu); -} - -static int amdgpu_bo_list_set(struct amdgpu_device *adev, - struct drm_file *filp, - struct amdgpu_bo_list *list, - struct drm_amdgpu_bo_list_entry *info, - unsigned num_entries) -{ - struct amdgpu_bo_list_entry *array; - struct amdgpu_bo *gds_obj = adev->gds.gds_gfx_bo; - struct amdgpu_bo *gws_obj = adev->gds.gws_gfx_bo; - struct amdgpu_bo *oa_obj = adev->gds.oa_gfx_bo; - - unsigned last_entry = 0, first_userptr = num_entries; - unsigned i; - int r; - unsigned long total_size = 0; + list->gds_obj = adev->gds.gds_gfx_bo; + list->gws_obj = adev->gds.gws_gfx_bo; + list->oa_obj = adev->gds.oa_gfx_bo; - array = kvmalloc_array(num_entries, sizeof(struct amdgpu_bo_list_entry), GFP_KERNEL); - if (!array) - return -ENOMEM; + array = amdgpu_bo_list_array_entry(list, 0); memset(array, 0, num_entries * sizeof(struct amdgpu_bo_list_entry)); for (i = 0; i < num_entries; ++i) { @@ -157,59 +118,56 @@ static int amdgpu_bo_list_set(struct amdgpu_device *adev, entry->tv.shared = !entry->robj->prime_shared_count; if (entry->robj->preferred_domains == AMDGPU_GEM_DOMAIN_GDS) - gds_obj = entry->robj; + list->gds_obj = entry->robj; if (entry->robj->preferred_domains == AMDGPU_GEM_DOMAIN_GWS) - gws_obj = entry->robj; + list->gws_obj = entry->robj; if (entry->robj->preferred_domains == AMDGPU_GEM_DOMAIN_OA) - oa_obj = entry->robj; + list->oa_obj = entry->robj; total_size += amdgpu_bo_size(entry->robj); trace_amdgpu_bo_list_set(list, entry->robj); } - for (i = 0; i < list->num_entries; ++i) - amdgpu_bo_unref(&list->array[i].robj); - - kvfree(list->array); - - list->gds_obj = gds_obj; - list->gws_obj = gws_obj; - list->oa_obj = oa_obj; list->first_userptr = first_userptr; - list->array = array; list->num_entries = num_entries; trace_amdgpu_cs_bo_status(list->num_entries, total_size); + + *result = list; return 0; error_free: while (i--) amdgpu_bo_unref(&array[i].robj); - kvfree(array); + kvfree(list); return r; + } -struct amdgpu_bo_list * -amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id) +static void amdgpu_bo_list_destroy(struct amdgpu_fpriv *fpriv, int id) { - struct amdgpu_bo_list *result; + struct amdgpu_bo_list *list; + + mutex_lock(&fpriv->bo_list_lock); + list = idr_remove(&fpriv->bo_list_handles, id); + mutex_unlock(&fpriv->bo_list_lock); + if (list) + kref_put(&list->refcount, amdgpu_bo_list_free); +} +int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id, + struct amdgpu_bo_list **result) +{ rcu_read_lock(); - result = idr_find(&fpriv->bo_list_handles, id); + *result = idr_find(&fpriv->bo_list_handles, id); - if (result) { - if (kref_get_unless_zero(&result->refcount)) { - rcu_read_unlock(); - mutex_lock(&result->lock); - } else { - rcu_read_unlock(); - result = NULL; - } - } else { + if (*result && kref_get_unless_zero(&(*result)->refcount)) { rcu_read_unlock(); + return 0; } - return result; + rcu_read_unlock(); + return -ENOENT; } void amdgpu_bo_list_get_list(struct amdgpu_bo_list *list, @@ -220,6 +178,7 @@ void amdgpu_bo_list_get_list(struct amdgpu_bo_list *list, * concatenated in descending order. */ struct list_head bucket[AMDGPU_BO_LIST_NUM_BUCKETS]; + struct amdgpu_bo_list_entry *e; unsigned i; for (i = 0; i < AMDGPU_BO_LIST_NUM_BUCKETS; i++) @@ -230,14 +189,13 @@ void amdgpu_bo_list_get_list(struct amdgpu_bo_list *list, * in the list, the sort mustn't change the ordering of buffers * with the same priority, i.e. it must be stable. */ - for (i = 0; i < list->num_entries; i++) { - unsigned priority = list->array[i].priority; + amdgpu_bo_list_for_each_entry(e, list) { + unsigned priority = e->priority; - if (!list->array[i].robj->parent) - list_add_tail(&list->array[i].tv.head, - &bucket[priority]); + if (!e->robj->parent) + list_add_tail(&e->tv.head, &bucket[priority]); - list->array[i].user_pages = NULL; + e->user_pages = NULL; } /* Connect the sorted buckets in the output list. */ @@ -247,71 +205,82 @@ void amdgpu_bo_list_get_list(struct amdgpu_bo_list *list, void amdgpu_bo_list_put(struct amdgpu_bo_list *list) { - mutex_unlock(&list->lock); - kref_put(&list->refcount, amdgpu_bo_list_release_rcu); -} - -void amdgpu_bo_list_free(struct amdgpu_bo_list *list) -{ - unsigned i; - - for (i = 0; i < list->num_entries; ++i) - amdgpu_bo_unref(&list->array[i].robj); - - mutex_destroy(&list->lock); - kvfree(list->array); - kfree(list); + kref_put(&list->refcount, amdgpu_bo_list_free); } -int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, - struct drm_file *filp) +int amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in, + struct drm_amdgpu_bo_list_entry **info_param) { + const void __user *uptr = u64_to_user_ptr(in->bo_info_ptr); const uint32_t info_size = sizeof(struct drm_amdgpu_bo_list_entry); - - struct amdgpu_device *adev = dev->dev_private; - struct amdgpu_fpriv *fpriv = filp->driver_priv; - union drm_amdgpu_bo_list *args = data; - uint32_t handle = args->in.list_handle; - const void __user *uptr = u64_to_user_ptr(args->in.bo_info_ptr); - struct drm_amdgpu_bo_list_entry *info; - struct amdgpu_bo_list *list; - int r; - info = kvmalloc_array(args->in.bo_number, - sizeof(struct drm_amdgpu_bo_list_entry), GFP_KERNEL); + info = kvmalloc_array(in->bo_number, info_size, GFP_KERNEL); if (!info) return -ENOMEM; /* copy the handle array from userspace to a kernel buffer */ r = -EFAULT; - if (likely(info_size == args->in.bo_info_size)) { - unsigned long bytes = args->in.bo_number * - args->in.bo_info_size; + if (likely(info_size == in->bo_info_size)) { + unsigned long bytes = in->bo_number * + in->bo_info_size; if (copy_from_user(info, uptr, bytes)) goto error_free; } else { - unsigned long bytes = min(args->in.bo_info_size, info_size); + unsigned long bytes = min(in->bo_info_size, info_size); unsigned i; - memset(info, 0, args->in.bo_number * info_size); - for (i = 0; i < args->in.bo_number; ++i) { + memset(info, 0, in->bo_number * info_size); + for (i = 0; i < in->bo_number; ++i) { if (copy_from_user(&info[i], uptr, bytes)) goto error_free; - uptr += args->in.bo_info_size; + uptr += in->bo_info_size; } } + *info_param = info; + return 0; + +error_free: + kvfree(info); + return r; +} + +int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct amdgpu_device *adev = dev->dev_private; + struct amdgpu_fpriv *fpriv = filp->driver_priv; + union drm_amdgpu_bo_list *args = data; + uint32_t handle = args->in.list_handle; + struct drm_amdgpu_bo_list_entry *info = NULL; + struct amdgpu_bo_list *list, *old; + int r; + + r = amdgpu_bo_create_list_entry_array(&args->in, &info); + if (r) + goto error_free; + switch (args->in.operation) { case AMDGPU_BO_LIST_OP_CREATE: r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number, - &handle); + &list); if (r) goto error_free; + + mutex_lock(&fpriv->bo_list_lock); + r = idr_alloc(&fpriv->bo_list_handles, list, 1, 0, GFP_KERNEL); + mutex_unlock(&fpriv->bo_list_lock); + if (r < 0) { + amdgpu_bo_list_put(list); + return r; + } + + handle = r; break; case AMDGPU_BO_LIST_OP_DESTROY: @@ -320,17 +289,22 @@ int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, break; case AMDGPU_BO_LIST_OP_UPDATE: - r = -ENOENT; - list = amdgpu_bo_list_get(fpriv, handle); - if (!list) + r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number, + &list); + if (r) goto error_free; - r = amdgpu_bo_list_set(adev, filp, list, info, - args->in.bo_number); - amdgpu_bo_list_put(list); - if (r) + mutex_lock(&fpriv->bo_list_lock); + old = idr_replace(&fpriv->bo_list_handles, list, handle); + mutex_unlock(&fpriv->bo_list_lock); + + if (IS_ERR(old)) { + amdgpu_bo_list_put(list); + r = PTR_ERR(old); goto error_free; + } + amdgpu_bo_list_put(old); break; default: @@ -345,6 +319,7 @@ int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, return 0; error_free: - kvfree(info); + if (info) + kvfree(info); return r; } |