// 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. *
*/
r = amdgpu_bo_reserve(vm->root.bo, false); if (r) return r;
va_map = amdgpu_vm_bo_lookup_mapping(vm, user_addr); if (!va_map) {
r = -EINVAL; goto out_err;
} /* Only validate the userq whether resident in the VM mapping range */ if (user_addr >= va_map->start &&
va_map->last - user_addr + 1 >= size) {
amdgpu_bo_unreserve(vm->root.bo); return 0;
}
r = -EINVAL;
out_err:
amdgpu_bo_unreserve(vm->root.bo); return r;
}
if (f && !dma_fence_is_signaled(f)) {
ret = dma_fence_wait_timeout(f, true, msecs_to_jiffies(100)); if (ret <= 0)
drm_file_err(uq_mgr->file, "Timed out waiting for fence=%llu:%llu\n",
f->context, f->seqno);
}
}
int
amdgpu_userq_active(struct amdgpu_userq_mgr *uq_mgr)
{ struct amdgpu_usermode_queue *queue; int queue_id; int ret = 0;
mutex_lock(&uq_mgr->userq_mutex); /* Resume all the queues for this process */
idr_for_each_entry(&uq_mgr->userq_idr, queue, queue_id)
ret += queue->state == AMDGPU_USERQ_STATE_MAPPED;
retry: /* Flush any pending resume work to create ev_fence */
flush_delayed_work(&uq_mgr->resume_work);
mutex_lock(&uq_mgr->userq_mutex);
spin_lock(&evf_mgr->ev_fence_lock);
ev_fence = evf_mgr->ev_fence;
spin_unlock(&evf_mgr->ev_fence_lock); if (!ev_fence || dma_fence_is_signaled(&ev_fence->base)) {
mutex_unlock(&uq_mgr->userq_mutex); /* * Looks like there was no pending resume work, * add one now to create a valid eviction fence
*/
schedule_delayed_work(&uq_mgr->resume_work, 0); goto retry;
}
}
int amdgpu_userq_create_object(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_userq_obj *userq_obj, int size)
{ struct amdgpu_device *adev = uq_mgr->adev; struct amdgpu_bo_param bp; int r;
r = amdgpu_bo_create(adev, &bp, &userq_obj->obj); if (r) {
drm_file_err(uq_mgr->file, "Failed to allocate BO for userqueue (%d)", r); return r;
}
r = amdgpu_bo_reserve(userq_obj->obj, true); if (r) {
drm_file_err(uq_mgr->file, "Failed to reserve BO to map (%d)", r); goto free_obj;
}
r = amdgpu_ttm_alloc_gart(&(userq_obj->obj)->tbo); if (r) {
drm_file_err(uq_mgr->file, "Failed to alloc GART for userqueue object (%d)", r); goto unresv;
}
r = amdgpu_bo_kmap(userq_obj->obj, &userq_obj->cpu_ptr); if (r) {
drm_file_err(uq_mgr->file, "Failed to map BO for userqueue (%d)", r); goto unresv;
}
r = amdgpu_bo_reserve(db_obj->obj, true); if (r) {
drm_file_err(uq_mgr->file, "[Usermode queues] Failed to pin doorbell object\n"); goto unref_bo;
}
/* Pin the BO before generating the index, unpin in queue destroy */
r = amdgpu_bo_pin(db_obj->obj, AMDGPU_GEM_DOMAIN_DOORBELL); if (r) {
drm_file_err(uq_mgr->file, "[Usermode queues] Failed to pin doorbell object\n"); goto unresv_bo;
}
switch (db_info->queue_type) { case AMDGPU_HW_IP_GFX: case AMDGPU_HW_IP_COMPUTE: case AMDGPU_HW_IP_DMA:
db_size = sizeof(u64); break;
r = amdgpu_userq_priority_permit(filp, priority); if (r) return r;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
drm_file_err(uq_mgr->file, "pm_runtime_get_sync() failed for userqueue create\n");
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
/* * There could be a situation that we are creating a new queue while * the other queues under this UQ_mgr are suspended. So if there is any * resume work pending, wait for it to get done. * * This will also make sure we have a valid eviction fence ready to be used.
*/
mutex_lock(&adev->userq_mutex);
amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
uq_funcs = adev->userq_funcs[args->in.ip_type]; if (!uq_funcs) {
drm_file_err(uq_mgr->file, "Usermode queue is not supported for this IP (%u)\n",
args->in.ip_type);
r = -EINVAL; goto unlock;
}
queue = kzalloc(sizeof(struct amdgpu_usermode_queue), GFP_KERNEL); if (!queue) {
drm_file_err(uq_mgr->file, "Failed to allocate memory for queue\n");
r = -ENOMEM; goto unlock;
}
/* Convert relative doorbell offset into absolute doorbell index */
index = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp); if (index == (uint64_t)-EINVAL) {
drm_file_err(uq_mgr->file, "Failed to get doorbell for queue\n");
kfree(queue);
r = -EINVAL; goto unlock;
}
queue->doorbell_index = index;
xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC);
r = amdgpu_userq_fence_driver_alloc(adev, queue); if (r) {
drm_file_err(uq_mgr->file, "Failed to alloc fence driver\n"); goto unlock;
}
r = uq_funcs->mqd_create(uq_mgr, &args->in, queue); if (r) {
drm_file_err(uq_mgr->file, "Failed to create Queue\n");
amdgpu_userq_fence_driver_free(queue);
kfree(queue); goto unlock;
}
qid = idr_alloc(&uq_mgr->userq_idr, queue, 1, AMDGPU_MAX_USERQ_COUNT, GFP_KERNEL); if (qid < 0) {
drm_file_err(uq_mgr->file, "Failed to allocate a queue id\n");
amdgpu_userq_fence_driver_free(queue);
uq_funcs->mqd_destroy(uq_mgr, queue);
kfree(queue);
r = -ENOMEM; goto unlock;
}
/* don't map the queue if scheduling is halted */ if (adev->userq_halt_for_enforce_isolation &&
((queue->queue_type == AMDGPU_HW_IP_GFX) ||
(queue->queue_type == AMDGPU_HW_IP_COMPUTE)))
skip_map_queue = true; else
skip_map_queue = false; if (!skip_map_queue) {
r = amdgpu_userq_map_helper(uq_mgr, queue); if (r) {
drm_file_err(uq_mgr->file, "Failed to map Queue\n");
idr_remove(&uq_mgr->userq_idr, qid);
amdgpu_userq_fence_driver_free(queue);
uq_funcs->mqd_destroy(uq_mgr, queue);
kfree(queue); goto unlock;
}
}
queue_name = kasprintf(GFP_KERNEL, "queue-%d", qid); if (!queue_name) {
r = -ENOMEM; goto unlock;
}
#ifdefined(CONFIG_DEBUG_FS) /* Queue dentry per client to hold MQD information */
queue->debugfs_queue = debugfs_create_dir(queue_name, filp->debugfs_client);
debugfs_create_file("mqd_info", 0444, queue->debugfs_queue, queue, &amdgpu_mqd_info_fops); #endif
kfree(queue_name);
switch (args->in.op) { case AMDGPU_USERQ_OP_CREATE: if (args->in.flags & ~(AMDGPU_USERQ_CREATE_FLAGS_QUEUE_PRIORITY_MASK |
AMDGPU_USERQ_CREATE_FLAGS_QUEUE_SECURE)) return -EINVAL; /* Usermode queues are only supported for GFX IP as of now */ if (args->in.ip_type != AMDGPU_HW_IP_GFX &&
args->in.ip_type != AMDGPU_HW_IP_DMA &&
args->in.ip_type != AMDGPU_HW_IP_COMPUTE) {
drm_file_err(filp, "Usermode queue doesn't support IP type %u\n",
args->in.ip_type); return -EINVAL;
}
if ((args->in.flags & AMDGPU_USERQ_CREATE_FLAGS_QUEUE_SECURE) &&
(args->in.ip_type != AMDGPU_HW_IP_GFX) &&
(args->in.ip_type != AMDGPU_HW_IP_COMPUTE) &&
!amdgpu_is_tmz(adev)) {
drm_file_err(filp, "Secure only supported on GFX/Compute queues\n"); return -EINVAL;
}
if (args->in.queue_va == AMDGPU_BO_INVALID_OFFSET ||
args->in.queue_va == 0 ||
args->in.queue_size == 0) {
drm_file_err(filp, "invalidate userq queue va or size\n"); return -EINVAL;
} if (!args->in.wptr_va || !args->in.rptr_va) {
drm_file_err(filp, "invalidate userq queue rptr or wptr\n"); return -EINVAL;
} break; case AMDGPU_USERQ_OP_FREE: if (args->in.ip_type ||
args->in.doorbell_handle ||
args->in.doorbell_offset ||
args->in.flags ||
args->in.queue_va ||
args->in.queue_size ||
args->in.rptr_va ||
args->in.wptr_va ||
args->in.wptr_va ||
args->in.mqd ||
args->in.mqd_size) return -EINVAL; break; default: return -EINVAL;
}
return 0;
}
int amdgpu_userq_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
{ union drm_amdgpu_userq *args = data; int r;
if (amdgpu_userq_input_args_validate(dev, args, filp) < 0) return -EINVAL;
switch (args->in.op) { case AMDGPU_USERQ_OP_CREATE:
r = amdgpu_userq_create(filp, args); if (r)
drm_file_err(filp, "Failed to create usermode queue\n"); break;
case AMDGPU_USERQ_OP_FREE:
r = amdgpu_userq_destroy(filp, args->in.queue_id); if (r)
drm_file_err(filp, "Failed to destroy usermode queue\n"); break;
default:
drm_dbg_driver(dev, "Invalid user queue op specified: %d\n", args->in.op); return -EINVAL;
}
return r;
}
staticint
amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr)
{ struct amdgpu_usermode_queue *queue; int queue_id; int ret = 0, r;
/* Resume all the queues for this process */
idr_for_each_entry(&uq_mgr->userq_idr, queue, queue_id) {
r = amdgpu_userq_map_helper(uq_mgr, queue); if (r)
ret = r;
}
if (ret)
drm_file_err(uq_mgr->file, "Failed to map all the queues\n"); return ret;
}
/* Per VM BOs never need to bo cleared in the page tables */
ret = amdgpu_vm_bo_update(adev, bo_va, false); if (ret) goto unlock_all;
spin_lock(&vm->status_lock);
}
bo = bo_va->base.bo;
ret = amdgpu_userq_validate_vm_bo(NULL, bo); if (ret) {
drm_file_err(uq_mgr->file, "Failed to validate BO\n"); goto unlock_all;
}
/* Try to reserve the BO to avoid clearing its ptes */ if (!adev->debug_vm && dma_resv_trylock(resv)) {
clear = false;
unlock = true; /* The caller is already holding the reservation lock */
} elseif (dma_resv_locking_ctx(resv) == ticket) {
clear = false;
unlock = false; /* Somebody else is using the BO right now */
} else {
clear = true;
unlock = false;
}
ret = amdgpu_vm_bo_update(adev, bo_va, clear);
if (unlock)
dma_resv_unlock(resv); if (ret) goto unlock_all;
ret = amdgpu_userq_validate_bos(uq_mgr); if (ret) {
drm_file_err(uq_mgr->file, "Failed to validate BOs to restore\n"); goto unlock;
}
ret = amdgpu_userq_restore_all(uq_mgr); if (ret) {
drm_file_err(uq_mgr->file, "Failed to restore all queues\n"); goto unlock;
}
unlock:
mutex_unlock(&uq_mgr->userq_mutex);
}
staticint
amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
{ struct amdgpu_usermode_queue *queue; int queue_id; int ret = 0, r;
/* Try to unmap all the queues in this process ctx */
idr_for_each_entry(&uq_mgr->userq_idr, queue, queue_id) {
r = amdgpu_userq_unmap_helper(uq_mgr, queue); if (r)
ret = r;
}
if (ret)
drm_file_err(uq_mgr->file, "Couldn't unmap all the queues\n"); return ret;
}
/* Wait for any pending userqueue fence work to finish */
ret = amdgpu_userq_wait_for_signal(uq_mgr); if (ret) {
drm_file_err(uq_mgr->file, "Not evicting userqueue, timeout waiting for work\n"); return;
}
ret = amdgpu_userq_evict_all(uq_mgr); if (ret) {
drm_file_err(uq_mgr->file, "Failed to evict userqueue\n"); return;
}
/* Signal current eviction fence */
amdgpu_eviction_fence_signal(evf_mgr, ev_fence);
if (evf_mgr->fd_closing) {
cancel_delayed_work_sync(&uq_mgr->resume_work); return;
}
/* Schedule a resume work */
schedule_delayed_work(&uq_mgr->resume_work, 0);
}
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.