diff options
author | Lukas Wunner <lukas@wunner.de> | 2018-02-11 04:38:28 -0500 |
---|---|---|
committer | Lukas Wunner <lukas@wunner.de> | 2018-02-16 16:40:23 -0500 |
commit | aa0aad57909eb321746325951d66af88a83bc956 (patch) | |
tree | 45a20e39488e683cddf0aa23939717dacf3851f4 /drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c | |
parent | 15734feff2bdac24aa3266c437cffa42851990e3 (diff) |
drm/amdgpu: Fix deadlock on runtime suspend
amdgpu's ->runtime_suspend hook calls drm_kms_helper_poll_disable(),
which waits for the output poll worker to finish if it's running.
The output poll worker meanwhile calls pm_runtime_get_sync() in
amdgpu's ->detect hooks, which waits for the ongoing suspend to finish,
causing a deadlock.
Fix by not acquiring a runtime PM ref if the ->detect hooks are called
in the output poll worker's context. This is safe because the poll
worker is only enabled while runtime active and we know that
->runtime_suspend waits for it to finish.
Fixes: d38ceaf99ed0 ("drm/amdgpu: add core driver (v4)")
Cc: stable@vger.kernel.org # v4.2+: 27d4ee03078a: workqueue: Allow retrieval of current task's work struct
Cc: stable@vger.kernel.org # v4.2+: 25c058ccaf2e: drm: Allow determining if current task is output poll worker
Cc: Alex Deucher <alexander.deucher@amd.com>
Tested-by: Mike Lothian <mike@fireburn.co.uk>
Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Link: https://patchwork.freedesktop.org/patch/msgid/4c9bf72aacae1eef062bd134cd112e0770a7f121.1518338789.git.lukas@wunner.de
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c | 58 |
1 files changed, 38 insertions, 20 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c index df9cbc78e168..21e7ae159dff 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c | |||
@@ -737,9 +737,11 @@ amdgpu_connector_lvds_detect(struct drm_connector *connector, bool force) | |||
737 | enum drm_connector_status ret = connector_status_disconnected; | 737 | enum drm_connector_status ret = connector_status_disconnected; |
738 | int r; | 738 | int r; |
739 | 739 | ||
740 | r = pm_runtime_get_sync(connector->dev->dev); | 740 | if (!drm_kms_helper_is_poll_worker()) { |
741 | if (r < 0) | 741 | r = pm_runtime_get_sync(connector->dev->dev); |
742 | return connector_status_disconnected; | 742 | if (r < 0) |
743 | return connector_status_disconnected; | ||
744 | } | ||
743 | 745 | ||
744 | if (encoder) { | 746 | if (encoder) { |
745 | struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | 747 | struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); |
@@ -758,8 +760,12 @@ amdgpu_connector_lvds_detect(struct drm_connector *connector, bool force) | |||
758 | /* check acpi lid status ??? */ | 760 | /* check acpi lid status ??? */ |
759 | 761 | ||
760 | amdgpu_connector_update_scratch_regs(connector, ret); | 762 | amdgpu_connector_update_scratch_regs(connector, ret); |
761 | pm_runtime_mark_last_busy(connector->dev->dev); | 763 | |
762 | pm_runtime_put_autosuspend(connector->dev->dev); | 764 | if (!drm_kms_helper_is_poll_worker()) { |
765 | pm_runtime_mark_last_busy(connector->dev->dev); | ||
766 | pm_runtime_put_autosuspend(connector->dev->dev); | ||
767 | } | ||
768 | |||
763 | return ret; | 769 | return ret; |
764 | } | 770 | } |
765 | 771 | ||
@@ -869,9 +875,11 @@ amdgpu_connector_vga_detect(struct drm_connector *connector, bool force) | |||
869 | enum drm_connector_status ret = connector_status_disconnected; | 875 | enum drm_connector_status ret = connector_status_disconnected; |
870 | int r; | 876 | int r; |
871 | 877 | ||
872 | r = pm_runtime_get_sync(connector->dev->dev); | 878 | if (!drm_kms_helper_is_poll_worker()) { |
873 | if (r < 0) | 879 | r = pm_runtime_get_sync(connector->dev->dev); |
874 | return connector_status_disconnected; | 880 | if (r < 0) |
881 | return connector_status_disconnected; | ||
882 | } | ||
875 | 883 | ||
876 | encoder = amdgpu_connector_best_single_encoder(connector); | 884 | encoder = amdgpu_connector_best_single_encoder(connector); |
877 | if (!encoder) | 885 | if (!encoder) |
@@ -925,8 +933,10 @@ amdgpu_connector_vga_detect(struct drm_connector *connector, bool force) | |||
925 | amdgpu_connector_update_scratch_regs(connector, ret); | 933 | amdgpu_connector_update_scratch_regs(connector, ret); |
926 | 934 | ||
927 | out: | 935 | out: |
928 | pm_runtime_mark_last_busy(connector->dev->dev); | 936 | if (!drm_kms_helper_is_poll_worker()) { |
929 | pm_runtime_put_autosuspend(connector->dev->dev); | 937 | pm_runtime_mark_last_busy(connector->dev->dev); |
938 | pm_runtime_put_autosuspend(connector->dev->dev); | ||
939 | } | ||
930 | 940 | ||
931 | return ret; | 941 | return ret; |
932 | } | 942 | } |
@@ -989,9 +999,11 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force) | |||
989 | enum drm_connector_status ret = connector_status_disconnected; | 999 | enum drm_connector_status ret = connector_status_disconnected; |
990 | bool dret = false, broken_edid = false; | 1000 | bool dret = false, broken_edid = false; |
991 | 1001 | ||
992 | r = pm_runtime_get_sync(connector->dev->dev); | 1002 | if (!drm_kms_helper_is_poll_worker()) { |
993 | if (r < 0) | 1003 | r = pm_runtime_get_sync(connector->dev->dev); |
994 | return connector_status_disconnected; | 1004 | if (r < 0) |
1005 | return connector_status_disconnected; | ||
1006 | } | ||
995 | 1007 | ||
996 | if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) { | 1008 | if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) { |
997 | ret = connector->status; | 1009 | ret = connector->status; |
@@ -1116,8 +1128,10 @@ out: | |||
1116 | amdgpu_connector_update_scratch_regs(connector, ret); | 1128 | amdgpu_connector_update_scratch_regs(connector, ret); |
1117 | 1129 | ||
1118 | exit: | 1130 | exit: |
1119 | pm_runtime_mark_last_busy(connector->dev->dev); | 1131 | if (!drm_kms_helper_is_poll_worker()) { |
1120 | pm_runtime_put_autosuspend(connector->dev->dev); | 1132 | pm_runtime_mark_last_busy(connector->dev->dev); |
1133 | pm_runtime_put_autosuspend(connector->dev->dev); | ||
1134 | } | ||
1121 | 1135 | ||
1122 | return ret; | 1136 | return ret; |
1123 | } | 1137 | } |
@@ -1360,9 +1374,11 @@ amdgpu_connector_dp_detect(struct drm_connector *connector, bool force) | |||
1360 | struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); | 1374 | struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); |
1361 | int r; | 1375 | int r; |
1362 | 1376 | ||
1363 | r = pm_runtime_get_sync(connector->dev->dev); | 1377 | if (!drm_kms_helper_is_poll_worker()) { |
1364 | if (r < 0) | 1378 | r = pm_runtime_get_sync(connector->dev->dev); |
1365 | return connector_status_disconnected; | 1379 | if (r < 0) |
1380 | return connector_status_disconnected; | ||
1381 | } | ||
1366 | 1382 | ||
1367 | if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) { | 1383 | if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) { |
1368 | ret = connector->status; | 1384 | ret = connector->status; |
@@ -1430,8 +1446,10 @@ amdgpu_connector_dp_detect(struct drm_connector *connector, bool force) | |||
1430 | 1446 | ||
1431 | amdgpu_connector_update_scratch_regs(connector, ret); | 1447 | amdgpu_connector_update_scratch_regs(connector, ret); |
1432 | out: | 1448 | out: |
1433 | pm_runtime_mark_last_busy(connector->dev->dev); | 1449 | if (!drm_kms_helper_is_poll_worker()) { |
1434 | pm_runtime_put_autosuspend(connector->dev->dev); | 1450 | pm_runtime_mark_last_busy(connector->dev->dev); |
1451 | pm_runtime_put_autosuspend(connector->dev->dev); | ||
1452 | } | ||
1435 | 1453 | ||
1436 | return ret; | 1454 | return ret; |
1437 | } | 1455 | } |