[PATCH] amdkfd: fix deadlock in set_perfcount

Zhu Lingshan <[email protected]> Fri, 31 Jul 2026 17:51:05 +0800
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
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.

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;
 }
 
-- 
2.53.0