Implement the AMDGPU_USERQ_OP_MODIFY ioctl operation to enable runtime updates of compute queues.
Suggested-by: Alex Deucher <[email protected]> Signed-off-by: Jesse Zhang <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 78 +++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 3 + 2 files changed, 81 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 256ceca6d429..eb1e27c27bde 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -837,6 +837,7 @@ static int amdgpu_userq_input_args_validate(struct drm_device *dev, switch (args->in.op) { case AMDGPU_USERQ_OP_CREATE: + case AMDGPU_USERQ_OP_MODIFY: if (args->in.flags & ~(AMDGPU_USERQ_CREATE_FLAGS_QUEUE_PRIORITY_MASK | AMDGPU_USERQ_CREATE_FLAGS_QUEUE_SECURE)) return -EINVAL; @@ -863,10 +864,29 @@ static int amdgpu_userq_input_args_validate(struct drm_device *dev, drm_file_err(filp, "invalidate userq queue va or size\n"); return -EINVAL; } + + if (!is_power_of_2(args->in.queue_size) && (args->in.queue_size != 0)) { + drm_file_err(filp, "Ring size must be a power of 2 or 0\n"); + return -EINVAL; + } + + if (args->in.queue_size > 0 && args->in.queue_size < AMDGPU_GPU_PAGE_SIZE) { + args->in.queue_size = AMDGPU_GPU_PAGE_SIZE; + drm_file_err(filp, "Size clamped to AMDGPU_GPU_PAGE_SIZE\n"); + } + + if ((args->in.queue_va) && + (!access_ok((const void __user *) args->in.queue_va, + sizeof(uint64_t)))) { + drm_file_err(filp, "Can't access ring base address\n"); + return -EFAULT; + } + 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 || @@ -901,6 +921,58 @@ bool amdgpu_userq_enabled(struct drm_device *dev) return false; } +static int amdgpu_modify_queue(struct drm_file *filp, union drm_amdgpu_userq *args) +{ + struct amdgpu_fpriv *fpriv = filp->driver_priv; + struct amdgpu_userq_mgr *uq_mgr = &fpriv->userq_mgr; + struct amdgpu_device *adev = uq_mgr->adev; + struct amdgpu_usermode_queue *queue; + const struct amdgpu_userq_funcs *userq_funcs; + int r; + + mutex_lock(&uq_mgr->userq_mutex); + queue = amdgpu_userq_find(uq_mgr, args->in.queue_id); + if (!queue) { + mutex_unlock(&uq_mgr->userq_mutex); + return -EINVAL; + } + + if (queue->queue_type != AMDGPU_HW_IP_COMPUTE) + return -EOPNOTSUPP; + + userq_funcs = adev->userq_funcs[queue->queue_type]; + + /* + * Unmap the queue if it's mapped or preempted to ensure a clean update. + * If the queue is already unmapped or hung, we skip this step. + */ + if (queue->state == AMDGPU_USERQ_STATE_MAPPED || + queue->state == AMDGPU_USERQ_STATE_PREEMPTED) { + r = amdgpu_userq_unmap_helper(queue); + if (r) + return r; + } + + r = userq_funcs->mqd_update(queue, &args->in); + if (r) + return r; + /* + * If the queue is considered active (has valid size, address, and percentage), + * we attempt to map it. This effectively starts the queue or restarts it + * if it was previously running. + */ + if (AMDGPU_USERQ_IS_ACTIVE(queue)) { + r = amdgpu_userq_map_helper(queue); + if (r) + drm_file_err(uq_mgr->file, "Failed to remap queue %llu after update\n", + queue->doorbell_index); + } + + mutex_unlock(&uq_mgr->userq_mutex); + + return r; +} + int amdgpu_userq_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { @@ -920,6 +992,12 @@ int amdgpu_userq_ioctl(struct drm_device *dev, void *data, drm_file_err(filp, "Failed to create usermode queue\n"); break; + + case AMDGPU_USERQ_OP_MODIFY: + r = amdgpu_modify_queue(filp, args); + if (r) + drm_file_err(filp, "Failed to modify usermode queue\n"); + break; case AMDGPU_USERQ_OP_FREE: r = amdgpu_userq_destroy(filp, args->in.queue_id); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index 833468b58603..7cd1ea94e368 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -31,6 +31,9 @@ #define to_ev_fence(f) container_of(f, struct amdgpu_eviction_fence, base) #define uq_mgr_to_fpriv(u) container_of(u, struct amdgpu_fpriv, userq_mgr) #define work_to_uq_mgr(w, name) container_of(w, struct amdgpu_userq_mgr, name) +#define AMDGPU_USERQ_IS_ACTIVE(q) ((q)->userq_prop->queue_size > 0 && \ + (q)->userq_prop->hqd_base_gpu_addr != 0 && \ + (q)->userq_prop->queue_percentage > 0) enum amdgpu_userq_state { AMDGPU_USERQ_STATE_UNMAPPED = 0, -- 2.49.0
