Extend the AMDGPU_USERQ ioctl to support dynamic modification of
existing user mode queues after creation. This provides userspace
with the ability to update queue attributes without requiring
destruction and recreation of queues.

v2: add a new op for AMDGPU_USERQ. E.g., AMDGPU_USERQ_OP_MODIFY. (Alex)
   make the target xcc an explicit parameter. (Alex)

Suggested-by: Alex Deucher <[email protected]>
Signed-off-by: Jesse Zhang <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu.h        |  3 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c  | 96 +++++++++++++++++++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h  |  3 +
 drivers/gpu/drm/amd/amdgpu/mes_userqueue.c |  1 +
 include/uapi/drm/amdgpu_drm.h              | 17 ++++
 5 files changed, 116 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index a8f4f73fa0ce..ad136145316b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -819,6 +819,9 @@ struct amdgpu_mqd_prop {
        uint32_t cu_mask_count;
        uint32_t cu_flags;
        bool is_user_cu_masked;
+       uint32_t queue_percentage;
+       /* used in gfx9 and gfx12.1 */
+       uint32_t pm4_target_xcc;
 };
 
 struct amdgpu_mqd {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index 4d7841f47dd3..de267135af69 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -34,6 +34,26 @@
 #include "amdgpu_hmm.h"
 #include "amdgpu_userq_fence.h"
 
+/* Mapping queue priority to pipe priority, indexed by queue priority */
+int amdgpu_userq_pipe_priority_map[] = {
+       AMDGPU_RING_PRIO_0,
+       AMDGPU_RING_PRIO_0,
+       AMDGPU_RING_PRIO_0,
+       AMDGPU_RING_PRIO_0,
+       AMDGPU_RING_PRIO_0,
+       AMDGPU_RING_PRIO_0,
+       AMDGPU_RING_PRIO_0,
+       AMDGPU_RING_PRIO_1,
+       AMDGPU_RING_PRIO_1,
+       AMDGPU_RING_PRIO_1,
+       AMDGPU_RING_PRIO_1,
+       AMDGPU_RING_PRIO_2,
+       AMDGPU_RING_PRIO_2,
+       AMDGPU_RING_PRIO_2,
+       AMDGPU_RING_PRIO_2,
+       AMDGPU_RING_PRIO_2
+};
+
 u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev)
 {
        int i;
@@ -906,7 +926,6 @@ static int amdgpu_userq_update_queue(struct 
amdgpu_usermode_queue *queue)
        struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr;
        struct amdgpu_device *adev = uq_mgr->adev;
        const struct amdgpu_userq_funcs *uq_funcs;
-       bool unmap_queue = false;
        int r;
 
        uq_funcs = adev->userq_funcs[queue->queue_type];
@@ -922,12 +941,17 @@ static int amdgpu_userq_update_queue(struct 
amdgpu_usermode_queue *queue)
                r = amdgpu_userq_unmap_helper(queue);
                if (r)
                        return r;
-               unmap_queue = true;
        }
 
        r = uq_funcs->mqd_update(queue);
-
-       if (unmap_queue) {
+       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",
@@ -937,6 +961,65 @@ static int amdgpu_userq_update_queue(struct 
amdgpu_usermode_queue *queue)
        return r;
 }
 
+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_usermode_queue *queue;
+       struct amdgpu_mqd_prop *props;
+       int r;
+
+       if (args->in.queue_percentage > AMDGPU_USERQ_MAX_QUEUE_PERCENTAGE) {
+               drm_file_err(uq_mgr->file, "Queue percentage must be between 0 
to AMDGPU_USERQ_MAX_QUEUE_PERCENTAGE\n");
+               return -EINVAL;
+       }
+
+       /* Validate priority */
+       if (args->in.hqd_queue_priority > AMDGPU_GFX_QUEUE_PRIORITY_MAXIMUM) {
+               drm_file_err(uq_mgr->file, "Queue priority must be between 0 to 
KFD_MAX_QUEUE_PRIORITY\n");
+               return -EINVAL;
+       }
+
+       /* Validate ring size */
+       if (!is_power_of_2(args->in.queue_size) && (args->in.queue_size != 0)) {
+               drm_file_err(uq_mgr->file, "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(uq_mgr->file, "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(uq_mgr->file, "Can't access ring base address\n");
+               return -EFAULT;
+       }
+
+       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;
+       }
+
+       props = queue->userq_prop;
+       props->queue_size = args->in.queue_size;
+       props->hqd_base_gpu_addr = args->in.queue_va;
+       props->queue_percentage = args->in.queue_percentage;
+       props->pm4_target_xcc = args->in.pm4_target_xcc;
+       props->hqd_pipe_priority = 
amdgpu_userq_pipe_priority_map[args->in.hqd_queue_priority];
+       props->hqd_queue_priority = args->in.hqd_queue_priority;
+
+       r = amdgpu_userq_update_queue(queue);
+
+       mutex_unlock(&uq_mgr->userq_mutex);
+
+       return r;
+}
+
 static int amdgpu_userq_set_cu_mask(struct drm_file *filp,  union 
drm_amdgpu_userq *args)
 {
        struct amdgpu_fpriv *fpriv = filp->driver_priv;
@@ -1031,6 +1114,11 @@ int amdgpu_userq_ioctl(struct drm_device *dev, void 
*data,
                amdgpu_userq_set_cu_mask(filp, args);
                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 c80d69368196..078f424e9415 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,
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c 
b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
index 0565986949bd..1a0079a2b47c 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
@@ -304,6 +304,7 @@ static int mes_userq_mqd_create(struct 
amdgpu_usermode_queue *queue,
        userq_props->use_doorbell = true;
        userq_props->doorbell_index = queue->doorbell_index;
        userq_props->fence_address = queue->fence_drv->gpu_addr;
+       userq_props->queue_percentage = AMDGPU_USERQ_MAX_QUEUE_PERCENTAGE;
 
        if (queue->queue_type == AMDGPU_HW_IP_COMPUTE) {
                struct drm_amdgpu_userq_mqd_compute_gfx11 *compute_mqd;
diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h
index cfc3a9313229..fab2914ac25c 100644
--- a/include/uapi/drm/amdgpu_drm.h
+++ b/include/uapi/drm/amdgpu_drm.h
@@ -331,6 +331,7 @@ union drm_amdgpu_ctx {
 #define AMDGPU_USERQ_OP_CREATE 1
 #define AMDGPU_USERQ_OP_FREE   2
 #define AMDGPU_USERQ_OP_MODIFY_CU_MASK 3
+#define AMDGPU_USERQ_OP_MODIFY 4
 
 /* queue priority levels */
 /* low < normal low < normal high < high */
@@ -342,6 +343,7 @@ union drm_amdgpu_ctx {
 #define AMDGPU_USERQ_CREATE_FLAGS_QUEUE_PRIORITY_HIGH 3 /* admin only */
 /* for queues that need access to protected content */
 #define AMDGPU_USERQ_CREATE_FLAGS_QUEUE_SECURE  (1 << 2)
+#define AMDGPU_USERQ_MAX_QUEUE_PERCENTAGE      100
 
 /*
  * This structure is a container to pass input configuration
@@ -423,6 +425,21 @@ struct drm_amdgpu_userq_in {
         * represents 32 CUs/WGPs)
         */
        __u32 cu_mask_count;
+       /**
+        * @queue_percentage: Queue resource allocation percentage (0-100)
+        * Defines the percentage of GPU resources allocated to this queue
+        */
+       __u32 queue_percentage;
+       /**
+        * @hqd_queue_priority: Hqd Queue priority (0-15)
+        * Higher values indicate higher scheduling priority for the queue
+        */
+       __u32 hqd_queue_priority;
+       /**
+        * @pm4_target_xcc: PM4 target XCC identifier (for gfx9/gfx12.1)
+        * Specifies the target XCC (Cross Compute Complex) for PM4 commands
+        */
+       __u32 pm4_target_xcc;
 };
 
 /* The structure to carry output of userqueue ops */
-- 
2.49.0

Reply via email to