Re: [PATCH] amdkfd: fix deadlock in set_perfcount

Felix Kuehling <[email protected]> Tue, 4 Aug 2026 18:41:18 -0400
Newsgroups org.freedesktop.lists.amd-gfx
Organization AMD Inc.
Message-ID <[email protected]>
[+Benjamin]

On 2026-07-31 05:51, Zhu Lingshan wrote:
> set_perfcount holds the DQM lock (dqm_lock(dqm)
> while walking queues, but pqm_update_mqd
> calls update_queue, which acquires the
> same lock by dqm_lock(dqm), causing deadlocks.
>
> This commit introduces update_queue_locked which assumes
> the dqm lock is already held, and update_queue calls it
> with proper lockings.
>
> set_perfcount calls update_queue_locked instead of pqm_update_mqd,
> therefore the deadlock is fixed.

I think the patch looks fine to me. I'm just wondering how this bug was 
not noticed in developer testing of this feature. I see a very similar 
patch already exists on the DKMS branch but somehow it's missing in 
amd-staging-drm-next:

commit 5cd0710732a45527b6398f8b522289f9adca708b
Author:     Benjamin Welton<[email protected]>
AuthorDate: Mon Jan 6 10:30:01 2025 -0800
Commit:     Chengjun Yao<[email protected]>
CommitDate: Mon Jun 22 12:51:06 2026 +0800

     amd/amdkfd: fix double lock aquisition in set_perfcount
     
     Seperates out locking from update_queue to allow updating
     of queues by code already holding the mqd lock. Fixes a
     hang in set_perfcount. This change was in the original
     mailing list commit for set_perfcount but was not included
     in gerrit.
     
     Fixes: b58289f0abf7 ("Add kfd_ioctl_profiler to contain profiler kernel driver changes")
     
     Signed-off-by: Benjamin Welton<[email protected]>
     Acked-by: Kent Russell<[email protected]>


Benjamin, do you know what's going on here? I think the above patch just 
needs to be ported to amd-staging-drm-next. But then I'm wondering what 
else is missing. Or maybe the whole feature was submitted to 
amd-staging-drm-next by mistake.

Regards,
   Felix


>
> Signed-off-by: Zhu Lingshan<[email protected]>
> ---
>   .../drm/amd/amdkfd/kfd_device_queue_manager.c | 44 ++++++++++++-------
>   1 file changed, 27 insertions(+), 17 deletions(-)
>
> 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 51ee9c39104b..c34c5f6a5541 100644
> --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
> @@ -77,6 +77,9 @@ static struct queue *find_queue_by_doorbell_offset(struct device_queue_manager *
>   static void set_queue_as_reset(struct device_queue_manager *dqm, struct queue *q,
>   			       struct qcm_process_device *qpd);
>   static int reset_queues_mes(struct device_queue_manager *dqm, struct queue *q);
> +static int update_queue_locked(struct device_queue_manager *dqm,
> +			       struct queue *q,
> +			       struct mqd_update_info *minfo);
>   
>   static inline
>   enum KFD_MQD_TYPE get_mqd_type_from_queue_type(enum kfd_queue_type type)
> @@ -341,8 +344,7 @@ static void set_perfcount(struct device_queue_manager *dqm, int enable)
>   	list_for_each_entry(cur, &dqm->queues, list) {
>   		qpd = cur->qpd;
>   		list_for_each_entry(q, &qpd->queues_list, list) {
> -			pqm_update_mqd(qpd->pqm, q->properties.queue_id,
> -						&minfo);
> +			update_queue_locked(dqm, q, &minfo);
>   		}
>   	}
>   	dqm_unlock(dqm);
> @@ -1059,8 +1061,9 @@ static int destroy_queue_nocpsch(struct device_queue_manager *dqm,
>   	return retval;
>   }
>   
> -static int update_queue(struct device_queue_manager *dqm, struct queue *q,
> -			struct mqd_update_info *minfo)
> +static int update_queue_locked(struct device_queue_manager *dqm,
> +			       struct queue *q,
> +			       struct mqd_update_info *minfo)
>   {
>   	int retval = 0;
>   	struct device *dev = dqm->dev->adev->dev;
> @@ -1068,12 +1071,11 @@ static int update_queue(struct device_queue_manager *dqm, struct queue *q,
>   	struct kfd_process_device *pdd;
>   	bool prev_active = false;
>   
> -	dqm_lock(dqm);
> +	lockdep_assert_held(&dqm->lock_hidden);
> +
>   	pdd = kfd_get_process_device_data(q->device, q->process);
> -	if (!pdd) {
> -		retval = -ENODEV;
> -		goto out_unlock;
> -	}
> +	if (!pdd)
> +		return -ENODEV;
>   	mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type(
>   			q->properties.type)];
>   
> @@ -1089,14 +1091,12 @@ static int update_queue(struct device_queue_manager *dqm, struct queue *q,
>   			retval = remove_queue_mes(dqm, q, &pdd->qpd);
>   
>   		/* queue is reset so inaccessable  */
> -		if (pdd->has_reset_queue) {
> -			retval = -EACCES;
> -			goto out_unlock;
> -		}
> +		if (pdd->has_reset_queue)
> +			return -EACCES;
>   
>   		if (retval) {
>   			dev_err(dev, "unmap queue failed\n");
> -			goto out_unlock;
> +			return retval;
>   		}
>   	} else if (prev_active &&
>   		   (q->properties.type == KFD_QUEUE_TYPE_COMPUTE ||
> @@ -1105,7 +1105,7 @@ static int update_queue(struct device_queue_manager *dqm, struct queue *q,
>   
>   		if (!dqm->sched_running) {
>   			WARN_ONCE(1, "Update non-HWS queue while stopped\n");
> -			goto out_unlock;
> +			return -EIO;
>   		}
>   
>   		retval = mqd_mgr->destroy_mqd(mqd_mgr, q->mqd,
> @@ -1115,7 +1115,7 @@ static int update_queue(struct device_queue_manager *dqm, struct queue *q,
>   				KFD_UNMAP_LATENCY_MS, q->pipe, q->queue);
>   		if (retval) {
>   			dev_err(dev, "destroy mqd failed\n");
> -			goto out_unlock;
> +			return retval;
>   		}
>   	}
>   
> @@ -1163,8 +1163,18 @@ static int update_queue(struct device_queue_manager *dqm, struct queue *q,
>   						   &q->properties, current->mm);
>   	}
>   
> -out_unlock:
> +	return retval;
> +}
> +
> +static int update_queue(struct device_queue_manager *dqm, struct queue *q,
> +			struct mqd_update_info *minfo)
> +{
> +	int retval;
> +
> +	dqm_lock(dqm);
> +	retval = update_queue_locked(dqm, q, minfo);
>   	dqm_unlock(dqm);
> +
>   	return retval;
>   }
>