[PATCH 1/4][SRU][J] drm/amd/display: Fork thread to offload work of hpd_rx_irq
Koba Ko
koba.ko at canonical.com
Fri Dec 16 01:33:40 UTC 2022
From: Wayne Lin <Wayne.Lin at amd.com>
BugLink: https://bugs.launchpad.net/bugs/1999836
[Why]
Currently, we will try to get dm.dc_lock in handle_hpd_rx_irq() when
link lost happened, which is risky and could cause deadlock.
e.g. If we are under procedure to enable MST streams and then monitor
happens to toggle short hpd to notify link lost, then
handle_hpd_rx_irq() will get blocked due to stream enabling flow has
dc_lock. However, under MST, enabling streams involves communication
with remote sinks which need to use handle_hpd_rx_irq() to handle
sideband messages. Thus, we have deadlock here.
[How]
Target is to have handle_hpd_rx_irq() finished as soon as possilble.
Hence we can react to interrupt quickly. Besides, we should avoid to
grabe dm.dc_lock within handle_hpd_rx_irq() to avoid deadlock situation.
Firstly, revert patches which introduced to use dm.dc_lock in
handle_hpd_rx_irq():
* commit ("drm/amd/display: NULL pointer error during ")
* commit ("drm/amd/display: Only one display lights up while using MST")
* commit ("drm/amd/display: take dc_lock in short pulse handler only")
Instead, create work to handle irq events which needs dm.dc_lock.
Besides:
* Create struct hpd_rx_irq_offload_work_queue for each link to handle
its short hpd events
* Avoid to handle link lost/ automated test if the link is disconnected
* Defer dc_lock needed works in dc_link_handle_hpd_rx_irq(). This
function should just handle simple stuff for us (e.g. DPCD R/W).
However, deferred works should still be handled by the order that
dc_link_handle_hpd_rx_irq() used to be.
* Change function name dm_handle_hpd_rx_irq() to
dm_handle_mst_sideband_msg() to be more specific
Reviewed-by: Nicholas Kazlauskas <Nicholas.Kazlauskas at amd.com>
Acked-by: Mikita Lipski <mikita.lipski at amd.com>
Signed-off-by: Wayne Lin <Wayne.Lin at amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler at amd.com>
Signed-off-by: Alex Deucher <alexander.deucher at amd.com>
(cherry picked from commit 8e794421bc981586d0af4e959ec76d668c793a55)
Signed-off-by: Koba Ko <koba.ko at canonical.com>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 76 +++++++++++++++++++
1 file changed, 76 insertions(+)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 7c5b63bfd371e..2e8d8a45e2ca5 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -1315,6 +1315,82 @@ static void retrieve_dmi_info(struct amdgpu_display_manager *dm)
}
}
+static void dm_handle_hpd_rx_offload_work(struct work_struct *work)
+{
+ struct hpd_rx_irq_offload_work *offload_work;
+ struct amdgpu_dm_connector *aconnector;
+ struct dc_link *dc_link;
+ struct amdgpu_device *adev;
+ enum dc_connection_type new_connection_type = dc_connection_none;
+ unsigned long flags;
+
+ offload_work = container_of(work, struct hpd_rx_irq_offload_work, work);
+ aconnector = offload_work->offload_wq->aconnector;
+
+ if (!aconnector) {
+ DRM_ERROR("Can't retrieve aconnector in hpd_rx_irq_offload_work");
+ goto skip;
+ }
+
+ adev = drm_to_adev(aconnector->base.dev);
+ dc_link = aconnector->dc_link;
+
+ mutex_lock(&aconnector->hpd_lock);
+ if (!dc_link_detect_sink(dc_link, &new_connection_type))
+ DRM_ERROR("KMS: Failed to detect connector\n");
+ mutex_unlock(&aconnector->hpd_lock);
+
+ if (new_connection_type == dc_connection_none)
+ goto skip;
+
+ if (amdgpu_in_reset(adev))
+ goto skip;
+
+ mutex_lock(&adev->dm.dc_lock);
+ if (offload_work->data.bytes.device_service_irq.bits.AUTOMATED_TEST)
+ dc_link_dp_handle_automated_test(dc_link);
+ else if ((dc_link->connector_signal != SIGNAL_TYPE_EDP) &&
+ hpd_rx_irq_check_link_loss_status(dc_link, &offload_work->data) &&
+ dc_link_dp_allow_hpd_rx_irq(dc_link)) {
+ dc_link_dp_handle_link_loss(dc_link);
+ spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags);
+ offload_work->offload_wq->is_handling_link_loss = false;
+ spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags);
+ }
+ mutex_unlock(&adev->dm.dc_lock);
+
+skip:
+ kfree(offload_work);
+
+}
+
+static struct hpd_rx_irq_offload_work_queue *hpd_rx_irq_create_workqueue(struct dc *dc)
+{
+ int max_caps = dc->caps.max_links;
+ int i = 0;
+ struct hpd_rx_irq_offload_work_queue *hpd_rx_offload_wq = NULL;
+
+ hpd_rx_offload_wq = kcalloc(max_caps, sizeof(*hpd_rx_offload_wq), GFP_KERNEL);
+
+ if (!hpd_rx_offload_wq)
+ return NULL;
+
+
+ for (i = 0; i < max_caps; i++) {
+ hpd_rx_offload_wq[i].wq =
+ create_singlethread_workqueue("amdgpu_dm_hpd_rx_offload_wq");
+
+ if (hpd_rx_offload_wq[i].wq == NULL) {
+ DRM_ERROR("create amdgpu_dm_hpd_rx_offload_wq fail!");
+ return NULL;
+ }
+
+ spin_lock_init(&hpd_rx_offload_wq[i].offload_lock);
+ }
+
+ return hpd_rx_offload_wq;
+}
+
static int amdgpu_dm_init(struct amdgpu_device *adev)
{
struct dc_init_data init_data;
--
2.25.1
More information about the kernel-team
mailing list