On Tue, Jan 27, 2026 at 2:57 AM Jesse.Zhang <[email protected]> wrote:
>
> 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;
> + }
Why 0? Also this should probably be a separate patch as it's more of
a fix than part of the modify change.
> +
> + 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");
> + }
Why not return an error here? Also this check should probably also be
part of another fix patch like above.
> +
> + if ((args->in.queue_va) &&
> + (!access_ok((const void __user *) args->in.queue_va,
> + sizeof(uint64_t)))) {
This isn't a CPU virtual address, it's a GPU virtual address. It may
not be a valid CPU virtual address.
> + 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;
We'll probably want to support this for non-compute queues as well for
consistency.
Alex
> +
> + 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
>