kfd_criu_restore_queue() takes the control stack size of a restored
queue from the checkpoint image, without the check create-queue does in
kfd_queue_acquire_buffers(). On gfx9 with CWSR, that size, per XCC,
sizes the MQD buffer in allocate_mqd() and is the ceiling
get_wave_state() uses to bound its copy of the control stack. A value of
0xFFFFFFFF reaches a kmalloc() above KMALLOC_MAX_SIZE and trips a page
allocator warning. Without CWSR the MQD comes from the GTT
sub-allocator, sized for the MQD alone, yet restore_mqd() still copies
the image's control stack one page after it.

Take the per-XCC control stack size from topology instead, as
create-queue requires, and 0 for queues without a control stack
(non-compute, or CWSR disabled). Reject an image that carries more
control stack than that per XCC.

The MQD in the image keeps its own cp_hqd_cntl_stack_size and
cp_hqd_cntl_stack_offset, which CWSR uses to place the control stack
after the MQD. Set the size to the allocated size in the gfx9
restore_mqd(), as init_mqd() does, and keep the offset within it, so
CWSR cannot access past the control stack of the new allocation.

Fixes: 3a9822d7bd62 ("drm/amdkfd: CRIU checkpoint and restore queue control 
stack")
Signed-off-by: William Palacek <[email protected]>
---
 .../gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c   |  3 ++
 .../amd/amdkfd/kfd_process_queue_manager.c    | 33 ++++++++++++++-----
 2 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c 
b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
index 6e6bc1ec0b64..c02dd9abc3ad 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
@@ -462,6 +462,9 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
        addr = mqd_mem_obj->gpu_addr;
 
        memcpy(m, mqd_src, sizeof(*m));
+       /* CWSR must not access past the control stack of this allocation */
+       m->cp_hqd_cntl_stack_size = qp->ctl_stack_size;
+       m->cp_hqd_cntl_stack_offset = min(m->cp_hqd_cntl_stack_offset, 
qp->ctl_stack_size);
 
        *mqd = m;
        if (gart_addr)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c 
b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
index a15a826519ad..b41494775767 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
@@ -26,6 +26,7 @@
 #include <linux/list.h>
 #include "kfd_device_queue_manager.h"
 #include "kfd_priv.h"
+#include "kfd_topology.h"
 #include "kfd_kernel_queue.h"
 #include "amdgpu_amdkfd.h"
 #include "amdgpu_reset.h"
@@ -983,7 +984,7 @@ int kfd_criu_checkpoint_queues(struct kfd_process *p,
 }
 
 static void set_queue_properties_from_criu(struct queue_properties *qp,
-                                         struct kfd_criu_queue_priv_data 
*q_data, uint32_t num_xcc)
+                                         struct kfd_criu_queue_priv_data 
*q_data)
 {
        qp->is_interop = false;
        qp->queue_percent = q_data->q_percent;
@@ -996,11 +997,6 @@ static void set_queue_properties_from_criu(struct 
queue_properties *qp,
        qp->eop_ring_buffer_size = q_data->eop_ring_buffer_size;
        qp->ctx_save_restore_area_address = 
q_data->ctx_save_restore_area_address;
        qp->ctx_save_restore_area_size = q_data->ctx_save_restore_area_size;
-       if (q_data->type == KFD_QUEUE_TYPE_COMPUTE)
-               qp->ctl_stack_size = q_data->ctl_stack_size / num_xcc;
-       else
-               qp->ctl_stack_size = q_data->ctl_stack_size;
-
        qp->type = q_data->type;
        qp->format = q_data->format;
 }
@@ -1012,6 +1008,7 @@ int kfd_criu_restore_queue(struct kfd_process *p,
 {
        uint8_t *mqd, *ctl_stack, *q_extra_data = NULL;
        struct kfd_criu_queue_priv_data *q_data;
+       struct kfd_topology_device *topo_dev;
        struct kfd_process_device *pdd;
        uint64_t q_extra_data_size;
        struct queue_properties qp;
@@ -1048,6 +1045,27 @@ int kfd_criu_restore_queue(struct kfd_process *p,
                goto exit;
        }
 
+       memset(&qp, 0, sizeof(qp));
+       set_queue_properties_from_criu(&qp, q_data);
+
+       /*
+        * Size the control stack as create-queue does, not from the image.
+        * Only compute queues with CWSR have one, one per XCC.
+        */
+       if (qp.type == KFD_QUEUE_TYPE_COMPUTE && pdd->dev->kfd->cwsr_enabled) {
+               topo_dev = kfd_topology_device_by_id(pdd->dev->id);
+               if (!topo_dev) {
+                       ret = -EINVAL;
+                       goto exit;
+               }
+               qp.ctl_stack_size = topo_dev->node_props.ctl_stack_size;
+       }
+
+       if (q_data->ctl_stack_size > (u64)qp.ctl_stack_size * 
NUM_XCC(pdd->dev->xcc_mask)) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
        *priv_data_offset += sizeof(*q_data);
        q_extra_data_size = (uint64_t)q_data->ctl_stack_size + q_data->mqd_size;
 
@@ -1077,9 +1095,6 @@ int kfd_criu_restore_queue(struct kfd_process *p,
        mqd = q_extra_data;
        ctl_stack = mqd + q_data->mqd_size;
 
-       memset(&qp, 0, sizeof(qp));
-       set_queue_properties_from_criu(&qp, q_data, 
NUM_XCC(pdd->dev->xcc_mask));
-
        print_queue_properties(&qp);
 
        ret = pqm_create_queue(&p->pqm, pdd->dev, &qp, &queue_id, q_data, mqd, 
ctl_stack, NULL);
-- 
2.43.0

Reply via email to