From: "Kuehling, Felix" <felix.kuehling@amd.com>
To: Shikang Fan <shikang.fan@amd.com>, amd-gfx@lists.freedesktop.org
Cc: Alexander.Deucher@amd.com, Christian.Koenig@amd.com,
Philip.Yang@amd.com, Mario.Limonciello@amd.com,
Srinivasan.Shanmugam@amd.com, Tiantian.Zhang@amd.com,
Victor.Zhao@amd.com, Guoqing.Zhang@amd.com
Subject: Re: [PATCH v5] drm/amdkfd: preserve VRAM MQD across hibernation via unpin/repin
Date: Thu, 6 Aug 2026 10:10:25 -0400 [thread overview]
Message-ID: <7557d4ea-68db-48ef-ab2d-7b8bc8f05061@amd.com> (raw)
In-Reply-To: <20260806062053.806535-1-shikang.fan@amd.com>
On 2026-08-06 02:20, Shikang Fan wrote:
> On gfx9 ASICs with mqd_on_vram(), a compute queue MQD lives in a pinned
> VRAM buffer object. Pinned BOs are skipped by the VRAM eviction done at S4
> suspend, so the MQD contents are lost across hibernation and the first
> submission after resume page-faults on a stale MQD.
>
> Unpin the MQD BO at suspend so the eviction migrates it into the
> hibernation image, and pin it back to VRAM on resume. The BO may return at
> a different VRAM address, so refresh the kernel mapping and cached GPU
> addresses and patch the MQD self-address via a new update_mqd_gpu_addr()
> mqd_manager op; skip eviction with a warning if that op is not implemented.
>
> v3: use unpin/repin instead of shadowing the MQD into a separate buffer.
>
> v4: drop the explicit VRAM->GTT placement at evict (a bare unpin is enough
> for the eviction pass to move the BO out of VRAM), and also repin at queue
> destroy. KFD queue restore runs late - user processes thaw before it, and
> under SR-IOV it is deferred until the VF regains full access - so once the
> VM has resumed an application can destroy a queue before its MQD BO is
> repinned, which would otherwise unpin an already-unpinned BO and touch a
> stale q->mqd.
>
> v5: drop support for no-HWS mode, and set q->mqd to NULL at eviction.
>
> Signed-off-by: Shikang Fan <shikang.fan@amd.com>
Reviewed-by: Felix Kuehling <felix.kuehling@amd.com>
> ---
> .../drm/amd/amdkfd/kfd_device_queue_manager.c | 106 ++++++++++++++++++
> drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h | 8 ++
> .../gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c | 40 +++++++
> drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 6 +
> 4 files changed, 160 insertions(+)
>
> diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
> index ea9d87450eae..a23384571193 100644
> --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
> @@ -1257,6 +1257,99 @@ static int resume_single_queue(struct device_queue_manager *dqm,
> return 0;
> }
>
> +/* Unpin the MQD BO at S4 suspend so it is evicted into the hibernation image;
> + * dqm_repin_mqd_bo() pins it back on resume. Gated on adev->in_s4 so runtime
> + * eviction is untouched.
> + */
> +static void dqm_evict_mqd_bo(struct device_queue_manager *dqm, struct queue *q)
> +{
> + struct mqd_manager *mqd_mgr;
> + struct amdgpu_bo *bo;
> +
> + if (!dqm->dev->adev->in_s4)
> + return;
> + if (!mqd_on_vram(dqm->dev->adev))
> + return;
> + if (q->properties.type != KFD_QUEUE_TYPE_COMPUTE)
> + return;
> + if (!q->mqd_mem_obj || !q->mqd_mem_obj->mem)
> + return;
> +
> + /* Without update_mqd_gpu_addr() the MQD self-address cannot be fixed up
> + * after a repin, so skip eviction (with a warning) instead of faulting.
> + */
> + mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type(q->properties.type)];
> + if (!mqd_mgr->update_mqd_gpu_addr) {
> + dev_warn_once(dqm->dev->adev->dev,
> + "MQD is in VRAM but update_mqd_gpu_addr is not implemented; skipping hibernation eviction\n");
> + return;
> + }
> +
> + bo = q->mqd_mem_obj->mem;
> + if (amdgpu_bo_reserve(bo, false))
> + return;
> +
> + amdgpu_bo_unpin(bo);
> + amdgpu_bo_unreserve(bo);
> + q->mqd = NULL;
> + q->needs_mqd_repin = true;
> +}
> +
> +/* Repin the MQD BO to VRAM and refresh the cached mapping and GPU addresses.
> + * Used both on resume and when a queue is destroyed before resume has repinned
> + * it. A no-op unless a repin is owed (needs_mqd_repin set).
> + */
> +static int dqm_repin_mqd_bo(struct device_queue_manager *dqm, struct queue *q)
> +{
> + struct mqd_manager *mqd_mgr;
> + struct amdgpu_bo *bo;
> + void *cpu_ptr;
> + int r;
> +
> + if (!q->needs_mqd_repin)
> + return 0;
> + if (!q->mqd_mem_obj || !q->mqd_mem_obj->mem)
> + return 0;
> +
> + bo = q->mqd_mem_obj->mem;
> + r = amdgpu_bo_reserve(bo, false);
> + if (r)
> + return r;
> + r = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_VRAM);
> + if (r) {
> + amdgpu_bo_unreserve(bo);
> + dev_err(dqm->dev->adev->dev,
> + "Failed to repin MQD of queue %d to VRAM: %d\n",
> + q->properties.queue_id, r);
> + return r;
> + }
> + /* The BO may have moved; refresh the kernel mapping and gpu address. */
> + amdgpu_bo_kunmap(bo);
> + r = amdgpu_bo_kmap(bo, &cpu_ptr);
> + amdgpu_bo_unreserve(bo);
> + if (r) {
> + dev_err(dqm->dev->adev->dev,
> + "Failed to remap MQD of queue %d: %d\n",
> + q->properties.queue_id, r);
> + return r;
> + }
> +
> + q->mqd_mem_obj->cpu_ptr = cpu_ptr;
> + q->mqd_mem_obj->gpu_addr = amdgpu_bo_gpu_offset(bo);
> + q->gart_mqd_addr = q->mqd_mem_obj->gpu_addr;
> + q->mqd = cpu_ptr;
> +
> + mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type(
> + q->properties.type)];
> + if (mqd_mgr->update_mqd_gpu_addr)
> + mqd_mgr->update_mqd_gpu_addr(mqd_mgr, q->mqd,
> + q->mqd_mem_obj,
> + &q->properties);
> +
> + q->needs_mqd_repin = false;
> + return 0;
> +}
> +
> static int evict_process_queues_nocpsch(struct device_queue_manager *dqm,
> struct qcm_process_device *qpd)
> {
> @@ -1353,6 +1446,8 @@ static int evict_process_queues_cpsch(struct device_queue_manager *dqm,
> goto out;
> }
> }
> +
> + dqm_evict_mqd_bo(dqm, q);
> }
>
> if (!dqm->dev->kfd->shared_resources.enable_mes) {
> @@ -1492,6 +1587,13 @@ static int restore_process_queues_cpsch(struct device_queue_manager *dqm,
> q->properties.is_active = true;
> increment_queue_count(dqm, &pdd->qpd, q);
>
> + retval = dqm_repin_mqd_bo(dqm, q);
> + if (retval) {
> + dev_err(dev, "Failed to repin MQD for queue %d\n",
> + q->properties.queue_id);
> + goto out;
> + }
> +
> if (dqm->dev->kfd->shared_resources.enable_mes) {
> retval = add_queue_mes(dqm, q, qpd);
> if (retval) {
> @@ -2763,6 +2865,8 @@ static int destroy_queue_cpsch(struct device_queue_manager *dqm,
> qpd->pqm->process, q->device,
> -1, false, NULL, 0);
>
> + /* Repin the MQD BO if still evicted for hibernation, before it is freed. */
> + dqm_repin_mqd_bo(dqm, q);
> mqd_mgr->free_mqd(mqd_mgr, q->mqd, q->mqd_mem_obj);
>
> return retval;
> @@ -3020,6 +3124,8 @@ static int process_termination_cpsch(struct device_queue_manager *dqm,
> list_del(&q->list);
> qpd->queue_count--;
> dqm_unlock(dqm);
> + /* Repin the MQD BO if still evicted for hibernation, before free. */
> + dqm_repin_mqd_bo(dqm, q);
> mqd_mgr->free_mqd(mqd_mgr, q->mqd, q->mqd_mem_obj);
> dqm_lock(dqm);
> }
> diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
> index 59eff3389d39..38b46b696243 100644
> --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
> @@ -117,6 +117,14 @@ struct mqd_manager {
> const void *ctl_stack_src,
> const u32 ctl_stack_size);
>
> + /* Patch the MQD's cached self GPU address after the MQD BO has moved
> + * (e.g. repinned to a new VRAM location on hibernation resume). The MQD
> + * contents are otherwise preserved.
> + */
> + void (*update_mqd_gpu_addr)(struct mqd_manager *mm, void *mqd,
> + struct kfd_mem_obj *mqd_mem_obj,
> + struct queue_properties *p);
> +
> #if defined(CONFIG_DEBUG_FS)
> int (*debugfs_show_mqd)(struct seq_file *m, void *data);
> #endif
> 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 75e5a9f67d50..b95720198e28 100644
> --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
> @@ -476,6 +476,20 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
> qp->is_active = 0;
> }
>
> +static void update_mqd_gpu_addr(struct mqd_manager *mm, void *mqd,
> + struct kfd_mem_obj *mqd_mem_obj,
> + struct queue_properties *qp)
> +{
> + struct v9_mqd *m = get_mqd(mqd);
> + uint64_t addr = mqd_mem_obj->gpu_addr;
> +
> + m->cp_mqd_base_addr_lo = lower_32_bits(addr);
> + m->cp_mqd_base_addr_hi = upper_32_bits(addr);
> +
> + if (mqd_on_vram(mm->dev->adev))
> + amdgpu_device_flush_hdp(mm->dev->adev, NULL);
> +}
> +
> static void init_mqd_hiq(struct mqd_manager *mm, void **mqd,
> struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr,
> struct queue_properties *q)
> @@ -860,6 +874,30 @@ static void restore_mqd_v9_4_3(struct mqd_manager *mm, void **mqd,
> if (mqd_on_vram(mm->dev->adev))
> amdgpu_device_flush_hdp(mm->dev->adev, NULL);
> }
> +
> +static void update_mqd_gpu_addr_v9_4_3(struct mqd_manager *mm, void *mqd,
> + struct kfd_mem_obj *mqd_mem_obj,
> + struct queue_properties *qp)
> +{
> + struct kfd_mem_obj xcc_mqd_mem_obj;
> + uint64_t offset = mm->mqd_stride(mm, qp);
> + u32 num_xcc = NUM_XCC(mm->dev->xcc_mask);
> + struct v9_mqd *m;
> + int xcc;
> +
> + memset(&xcc_mqd_mem_obj, 0x0, sizeof(struct kfd_mem_obj));
> +
> + for (xcc = 0; xcc < num_xcc; xcc++) {
> + get_xcc_mqd(mqd_mem_obj, &xcc_mqd_mem_obj, offset * xcc);
> + m = get_mqd(mqd + offset * xcc);
> + m->cp_mqd_base_addr_lo = lower_32_bits(xcc_mqd_mem_obj.gpu_addr);
> + m->cp_mqd_base_addr_hi = upper_32_bits(xcc_mqd_mem_obj.gpu_addr);
> + }
> +
> + if (mqd_on_vram(mm->dev->adev))
> + amdgpu_device_flush_hdp(mm->dev->adev, NULL);
> +}
> +
> static int destroy_mqd_v9_4_3(struct mqd_manager *mm, void *mqd,
> enum kfd_preempt_type type, unsigned int timeout,
> uint32_t pipe_id, uint32_t queue_id)
> @@ -1017,6 +1055,7 @@ struct mqd_manager *mqd_manager_init_v9(enum KFD_MQD_TYPE type,
> mqd->get_wave_state = get_wave_state_v9_4_3;
> mqd->checkpoint_mqd = checkpoint_mqd_v9_4_3;
> mqd->restore_mqd = restore_mqd_v9_4_3;
> + mqd->update_mqd_gpu_addr = update_mqd_gpu_addr_v9_4_3;
> } else {
> mqd->init_mqd = init_mqd;
> mqd->load_mqd = load_mqd;
> @@ -1025,6 +1064,7 @@ struct mqd_manager *mqd_manager_init_v9(enum KFD_MQD_TYPE type,
> mqd->get_wave_state = get_wave_state;
> mqd->checkpoint_mqd = checkpoint_mqd;
> mqd->restore_mqd = restore_mqd;
> + mqd->update_mqd_gpu_addr = update_mqd_gpu_addr;
> }
> break;
> case KFD_MQD_TYPE_HIQ:
> diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
> index 2ea1cfd330a9..d8631847f0eb 100644
> --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
> @@ -638,6 +638,12 @@ struct queue {
> uint32_t gang_ctx_array_index;
>
> struct amdgpu_bo *wptr_bo_gart;
> +
> + /* The VRAM-resident MQD BO (mqd_on_vram()) is unpinned at S4 suspend so
> + * TTM evicts it into the hibernation image, and repinned on resume. Set
> + * while the BO is unpinned so the resume path knows to repin it.
> + */
> + bool needs_mqd_repin;
> };
>
> enum KFD_MQD_TYPE {
prev parent reply other threads:[~2026-08-06 14:10 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-06 6:20 [PATCH v5] drm/amdkfd: preserve VRAM MQD across hibernation via unpin/repin Shikang Fan
2026-08-06 14:10 ` Kuehling, Felix [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=7557d4ea-68db-48ef-ab2d-7b8bc8f05061@amd.com \
--to=felix.kuehling@amd.com \
--cc=Alexander.Deucher@amd.com \
--cc=Christian.Koenig@amd.com \
--cc=Guoqing.Zhang@amd.com \
--cc=Mario.Limonciello@amd.com \
--cc=Philip.Yang@amd.com \
--cc=Srinivasan.Shanmugam@amd.com \
--cc=Tiantian.Zhang@amd.com \
--cc=Victor.Zhao@amd.com \
--cc=amd-gfx@lists.freedesktop.org \
--cc=shikang.fan@amd.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox