diff options
Diffstat (limited to 'kernel/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c')
-rw-r--r-- | kernel/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/kernel/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/kernel/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c new file mode 100644 index 000000000..f82a2dd83 --- /dev/null +++ b/kernel/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c @@ -0,0 +1,271 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: + * Christian König <deathsimple@vodafone.de> + */ + +#include <drm/drmP.h> +#include "amdgpu.h" +#include "amdgpu_trace.h" + +static int amdgpu_bo_list_create(struct amdgpu_fpriv *fpriv, + struct amdgpu_bo_list **result, + int *id) +{ + int r; + + *result = kzalloc(sizeof(struct amdgpu_bo_list), GFP_KERNEL); + if (!*result) + return -ENOMEM; + + mutex_lock(&fpriv->bo_list_lock); + r = idr_alloc(&fpriv->bo_list_handles, *result, + 1, 0, GFP_KERNEL); + if (r < 0) { + mutex_unlock(&fpriv->bo_list_lock); + kfree(*result); + return r; + } + *id = r; + + mutex_init(&(*result)->lock); + (*result)->num_entries = 0; + (*result)->array = NULL; + + mutex_lock(&(*result)->lock); + mutex_unlock(&fpriv->bo_list_lock); + + 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_find(&fpriv->bo_list_handles, id); + if (list) { + mutex_lock(&list->lock); + idr_remove(&fpriv->bo_list_handles, id); + mutex_unlock(&list->lock); + amdgpu_bo_list_free(list); + } + mutex_unlock(&fpriv->bo_list_lock); +} + +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; + + bool has_userptr = false; + unsigned i; + + array = drm_malloc_ab(num_entries, sizeof(struct amdgpu_bo_list_entry)); + if (!array) + return -ENOMEM; + memset(array, 0, num_entries * sizeof(struct amdgpu_bo_list_entry)); + + for (i = 0; i < num_entries; ++i) { + struct amdgpu_bo_list_entry *entry = &array[i]; + struct drm_gem_object *gobj; + + gobj = drm_gem_object_lookup(adev->ddev, filp, info[i].bo_handle); + if (!gobj) + goto error_free; + + entry->robj = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj)); + drm_gem_object_unreference_unlocked(gobj); + entry->priority = info[i].bo_priority; + entry->prefered_domains = entry->robj->initial_domain; + entry->allowed_domains = entry->prefered_domains; + if (entry->allowed_domains == AMDGPU_GEM_DOMAIN_VRAM) + entry->allowed_domains |= AMDGPU_GEM_DOMAIN_GTT; + if (amdgpu_ttm_tt_has_userptr(entry->robj->tbo.ttm)) { + has_userptr = true; + entry->prefered_domains = AMDGPU_GEM_DOMAIN_GTT; + entry->allowed_domains = AMDGPU_GEM_DOMAIN_GTT; + } + entry->tv.bo = &entry->robj->tbo; + entry->tv.shared = true; + + if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_GDS) + gds_obj = entry->robj; + if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_GWS) + gws_obj = entry->robj; + if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_OA) + oa_obj = 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); + + drm_free_large(list->array); + + list->gds_obj = gds_obj; + list->gws_obj = gws_obj; + list->oa_obj = oa_obj; + list->has_userptr = has_userptr; + list->array = array; + list->num_entries = num_entries; + + return 0; + +error_free: + drm_free_large(array); + return -ENOENT; +} + +struct amdgpu_bo_list * +amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id) +{ + struct amdgpu_bo_list *result; + + mutex_lock(&fpriv->bo_list_lock); + result = idr_find(&fpriv->bo_list_handles, id); + if (result) + mutex_lock(&result->lock); + mutex_unlock(&fpriv->bo_list_lock); + return result; +} + +void amdgpu_bo_list_put(struct amdgpu_bo_list *list) +{ + mutex_unlock(&list->lock); +} + +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); + drm_free_large(list->array); + kfree(list); +} + +int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + 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 = (const void*)(long)args->in.bo_info_ptr; + + struct drm_amdgpu_bo_list_entry *info; + struct amdgpu_bo_list *list; + + int r; + + info = drm_malloc_ab(args->in.bo_number, + sizeof(struct drm_amdgpu_bo_list_entry)); + 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 (copy_from_user(info, uptr, bytes)) + goto error_free; + + } else { + unsigned long bytes = min(args->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) { + if (copy_from_user(&info[i], uptr, bytes)) + goto error_free; + + uptr += args->in.bo_info_size; + } + } + + switch (args->in.operation) { + case AMDGPU_BO_LIST_OP_CREATE: + r = amdgpu_bo_list_create(fpriv, &list, &handle); + 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) + goto error_free; + + break; + + case AMDGPU_BO_LIST_OP_DESTROY: + amdgpu_bo_list_destroy(fpriv, handle); + handle = 0; + break; + + case AMDGPU_BO_LIST_OP_UPDATE: + r = -ENOENT; + list = amdgpu_bo_list_get(fpriv, handle); + if (!list) + goto error_free; + + r = amdgpu_bo_list_set(adev, filp, list, info, + args->in.bo_number); + amdgpu_bo_list_put(list); + if (r) + goto error_free; + + break; + + default: + r = -EINVAL; + goto error_free; + } + + memset(args, 0, sizeof(*args)); + args->out.list_handle = handle; + drm_free_large(info); + + return 0; + +error_free: + drm_free_large(info); + return r; +} |