// SPDX-License-Identifier: MIT /* * Copyright 2023 Advanced Micro Devices, Inc. * * 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, sublicense, * 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 above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * 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 NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. *
*/
void
amdgpu_userq_fence_driver_free(struct amdgpu_usermode_queue *userq)
{
amdgpu_userq_walk_and_drop_fence_drv(&userq->fence_drv_xa);
xa_destroy(&userq->fence_drv_xa); /* Drop the fence_drv reference held by user queue */
amdgpu_userq_fence_driver_put(userq->fence_drv);
}
/* Check if hardware has already processed the job */
spin_lock_irqsave(&fence_drv->fence_list_lock, flags); if (!dma_fence_is_signaled(fence))
list_add_tail(&userq_fence->link, &fence_drv->fences); else
dma_fence_put(fence);
/** * amdgpu_userq_fence_read_wptr - Read the userq wptr value * * @queue: user mode queue structure pointer * @wptr: write pointer value * * Read the wptr value from userq's MQD. The userq signal IOCTL * creates a dma_fence for the shared buffers that expects the * RPTR value written to seq64 memory >= WPTR. * * Returns wptr value on success, error on failure.
*/ staticint amdgpu_userq_fence_read_wptr(struct amdgpu_usermode_queue *queue,
u64 *wptr)
{ struct amdgpu_bo_va_mapping *mapping; struct amdgpu_bo *bo;
u64 addr, *ptr; int r;
r = amdgpu_bo_reserve(queue->vm->root.bo, false); if (r) return r;
mapping = amdgpu_vm_bo_lookup_mapping(queue->vm, addr >> PAGE_SHIFT); if (!mapping) {
amdgpu_bo_unreserve(queue->vm->root.bo);
DRM_ERROR("Failed to lookup amdgpu_bo_va_mapping\n"); return -EINVAL;
}
bo = amdgpu_bo_ref(mapping->bo_va->base.bo);
amdgpu_bo_unreserve(queue->vm->root.bo);
r = amdgpu_bo_reserve(bo, true); if (r) {
DRM_ERROR("Failed to reserve userqueue wptr bo"); return r;
}
r = amdgpu_bo_kmap(bo, (void **)&ptr); if (r) {
DRM_ERROR("Failed mapping the userqueue wptr bo"); goto map_error;
}
/* Array of pointers to the looked up syncobjs */
syncobj = kmalloc_array(num_syncobj_handles, sizeof(*syncobj), GFP_KERNEL); if (!syncobj) {
r = -ENOMEM; goto free_syncobj_handles;
}
for (entry = 0; entry < num_syncobj_handles; entry++) {
syncobj[entry] = drm_syncobj_find(filp, syncobj_handles[entry]); if (!syncobj[entry]) {
r = -ENOENT; goto free_syncobj;
}
}
num_read_bo_handles = args->num_bo_read_handles;
bo_handles_read = memdup_user(u64_to_user_ptr(args->bo_read_handles), sizeof(u32) * num_read_bo_handles); if (IS_ERR(bo_handles_read)) {
r = PTR_ERR(bo_handles_read); goto free_syncobj;
}
/* Array of pointers to the GEM read objects */
gobj_read = kmalloc_array(num_read_bo_handles, sizeof(*gobj_read), GFP_KERNEL); if (!gobj_read) {
r = -ENOMEM; goto free_bo_handles_read;
}
for (rentry = 0; rentry < num_read_bo_handles; rentry++) {
gobj_read[rentry] = drm_gem_object_lookup(filp, bo_handles_read[rentry]); if (!gobj_read[rentry]) {
r = -ENOENT; goto put_gobj_read;
}
}
num_write_bo_handles = args->num_bo_write_handles;
bo_handles_write = memdup_user(u64_to_user_ptr(args->bo_write_handles), sizeof(u32) * num_write_bo_handles); if (IS_ERR(bo_handles_write)) {
r = PTR_ERR(bo_handles_write); goto put_gobj_read;
}
/* Array of pointers to the GEM write objects */
gobj_write = kmalloc_array(num_write_bo_handles, sizeof(*gobj_write), GFP_KERNEL); if (!gobj_write) {
r = -ENOMEM; goto free_bo_handles_write;
}
for (wentry = 0; wentry < num_write_bo_handles; wentry++) {
gobj_write[wentry] = drm_gem_object_lookup(filp, bo_handles_write[wentry]); if (!gobj_write[wentry]) {
r = -ENOENT; goto put_gobj_write;
}
}
/* Retrieve the user queue */
queue = idr_find(&userq_mgr->userq_idr, args->queue_id); if (!queue) {
r = -ENOENT; goto put_gobj_write;
}
r = amdgpu_userq_fence_read_wptr(queue, &wptr); if (r) goto put_gobj_write;
r = amdgpu_userq_fence_alloc(&userq_fence); if (r) goto put_gobj_write;
/* We are here means UQ is active, make sure the eviction fence is valid */
amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
/* Create a new fence */
r = amdgpu_userq_fence_create(queue, userq_fence, wptr, &fence); if (r) {
mutex_unlock(&userq_mgr->userq_mutex);
kmem_cache_free(amdgpu_userq_fence_slab, userq_fence); goto put_gobj_write;
}
/* Lock all BOs with retry handling */
drm_exec_until_all_locked(&exec) {
r = drm_exec_prepare_array(&exec, gobj_read, num_read_bo_handles, 1);
drm_exec_retry_on_contention(&exec); if (r) {
drm_exec_fini(&exec); goto put_gobj_write;
}
r = drm_exec_prepare_array(&exec, gobj_write, num_write_bo_handles, 1);
drm_exec_retry_on_contention(&exec); if (r) {
drm_exec_fini(&exec); goto put_gobj_write;
}
}
if (!wait_info->num_fences) { if (num_points) { struct dma_fence_unwrap iter; struct dma_fence *fence; struct dma_fence *f;
for (i = 0; i < num_points; i++) {
r = drm_syncobj_find_fence(filp, timeline_handles[i],
timeline_points[i],
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT,
&fence); if (r) goto exec_fini;
/* * Passing num_fences = 0 means that userspace doesn't want to * retrieve userq_fence_info. If num_fences = 0 we skip filling * userq_fence_info and return the actual number of fences on * args->num_fences.
*/
wait_info->num_fences = num_fences;
} else { /* Array of fence info */
fence_info = kmalloc_array(wait_info->num_fences, sizeof(*fence_info), GFP_KERNEL); if (!fence_info) {
r = -ENOMEM; goto exec_fini;
}
/* Array of fences */
fences = kmalloc_array(wait_info->num_fences, sizeof(*fences), GFP_KERNEL); if (!fences) {
r = -ENOMEM; goto free_fence_info;
}
/* Retrieve GEM read objects fence */ for (i = 0; i < num_read_bo_handles; i++) { struct dma_resv_iter resv_cursor; struct dma_fence *fence;
dma_resv_for_each_fence(&resv_cursor, gobj_read[i]->resv,
DMA_RESV_USAGE_READ, fence) { if (WARN_ON_ONCE(num_fences >= wait_info->num_fences)) {
r = -EINVAL; goto free_fences;
}
for (i = 0; i < num_points; i++) {
r = drm_syncobj_find_fence(filp, timeline_handles[i],
timeline_points[i],
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT,
&fence); if (r) goto free_fences;
dma_fence_unwrap_for_each(f, &iter, fence) { if (WARN_ON_ONCE(num_fences >= wait_info->num_fences)) {
r = -EINVAL; goto free_fences;
}
dma_fence_get(f);
fences[num_fences++] = f;
}
dma_fence_put(fence);
}
}
/* Retrieve syncobj's fence */ for (i = 0; i < num_syncobj; i++) { struct dma_fence *fence;
r = drm_syncobj_find_fence(filp, syncobj_handles[i],
0,
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT,
&fence); if (r) goto free_fences;
if (WARN_ON_ONCE(num_fences >= wait_info->num_fences)) {
r = -EINVAL; goto free_fences;
}
fences[num_fences++] = fence;
}
/* * Keep only the latest fences to reduce the number of values * given back to userspace.
*/
num_fences = dma_fence_dedup_array(fences, num_fences);
waitq = idr_find(&userq_mgr->userq_idr, wait_info->waitq_id); if (!waitq) {
r = -EINVAL; goto free_fences;
}
for (i = 0, cnt = 0; i < num_fences; i++) { struct amdgpu_userq_fence_driver *fence_drv; struct amdgpu_userq_fence *userq_fence;
u32 index;
userq_fence = to_amdgpu_userq_fence(fences[i]); if (!userq_fence) { /* * Just waiting on other driver fences should * be good for now
*/
r = dma_fence_wait(fences[i], true); if (r) {
dma_fence_put(fences[i]); goto free_fences;
}
dma_fence_put(fences[i]); continue;
}
fence_drv = userq_fence->fence_drv; /* * We need to make sure the user queue release their reference * to the fence drivers at some point before queue destruction. * Otherwise, we would gather those references until we don't * have any more space left and crash.
*/
r = xa_alloc(&waitq->fence_drv_xa, &index, fence_drv,
xa_limit_32b, GFP_KERNEL); if (r) goto free_fences;
amdgpu_userq_fence_driver_get(fence_drv);
/* Store drm syncobj's gpu va address and value */
fence_info[cnt].va = fence_drv->va;
fence_info[cnt].value = fences[i]->seqno;
dma_fence_put(fences[i]); /* Increment the actual userq fence count */
cnt++;
}
wait_info->num_fences = cnt; /* Copy userq fence info to user space */ if (copy_to_user(u64_to_user_ptr(wait_info->out_fences),
fence_info, wait_info->num_fences * sizeof(*fence_info))) {
r = -EFAULT; goto free_fences;
}
kfree(fences);
kfree(fence_info);
}
drm_exec_fini(&exec); for (i = 0; i < num_read_bo_handles; i++)
drm_gem_object_put(gobj_read[i]);
kfree(gobj_read);
for (i = 0; i < num_write_bo_handles; i++)
drm_gem_object_put(gobj_write[i]);
kfree(gobj_write);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.