]>
Commit | Line | Data |
---|---|---|
bbecac5b GKH |
1 | From aa0aad57909eb321746325951d66af88a83bc956 Mon Sep 17 00:00:00 2001 |
2 | From: Lukas Wunner <lukas@wunner.de> | |
3 | Date: Sun, 11 Feb 2018 10:38:28 +0100 | |
4 | Subject: drm/amdgpu: Fix deadlock on runtime suspend | |
5 | ||
6 | From: Lukas Wunner <lukas@wunner.de> | |
7 | ||
8 | commit aa0aad57909eb321746325951d66af88a83bc956 upstream. | |
9 | ||
10 | amdgpu's ->runtime_suspend hook calls drm_kms_helper_poll_disable(), | |
11 | which waits for the output poll worker to finish if it's running. | |
12 | ||
13 | The output poll worker meanwhile calls pm_runtime_get_sync() in | |
14 | amdgpu's ->detect hooks, which waits for the ongoing suspend to finish, | |
15 | causing a deadlock. | |
16 | ||
17 | Fix by not acquiring a runtime PM ref if the ->detect hooks are called | |
18 | in the output poll worker's context. This is safe because the poll | |
19 | worker is only enabled while runtime active and we know that | |
20 | ->runtime_suspend waits for it to finish. | |
21 | ||
22 | Fixes: d38ceaf99ed0 ("drm/amdgpu: add core driver (v4)") | |
23 | Cc: stable@vger.kernel.org # v4.2+: 27d4ee03078a: workqueue: Allow retrieval of current task's work struct | |
24 | Cc: stable@vger.kernel.org # v4.2+: 25c058ccaf2e: drm: Allow determining if current task is output poll worker | |
25 | Cc: Alex Deucher <alexander.deucher@amd.com> | |
26 | Tested-by: Mike Lothian <mike@fireburn.co.uk> | |
27 | Reviewed-by: Lyude Paul <lyude@redhat.com> | |
28 | Signed-off-by: Lukas Wunner <lukas@wunner.de> | |
29 | Link: https://patchwork.freedesktop.org/patch/msgid/4c9bf72aacae1eef062bd134cd112e0770a7f121.1518338789.git.lukas@wunner.de | |
30 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
31 | ||
32 | --- | |
33 | drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c | 58 ++++++++++++++++--------- | |
34 | 1 file changed, 38 insertions(+), 20 deletions(-) | |
35 | ||
36 | --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c | |
37 | +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c | |
38 | @@ -737,9 +737,11 @@ amdgpu_connector_lvds_detect(struct drm_ | |
39 | enum drm_connector_status ret = connector_status_disconnected; | |
40 | int r; | |
41 | ||
42 | - r = pm_runtime_get_sync(connector->dev->dev); | |
43 | - if (r < 0) | |
44 | - return connector_status_disconnected; | |
45 | + if (!drm_kms_helper_is_poll_worker()) { | |
46 | + r = pm_runtime_get_sync(connector->dev->dev); | |
47 | + if (r < 0) | |
48 | + return connector_status_disconnected; | |
49 | + } | |
50 | ||
51 | if (encoder) { | |
52 | struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
53 | @@ -758,8 +760,12 @@ amdgpu_connector_lvds_detect(struct drm_ | |
54 | /* check acpi lid status ??? */ | |
55 | ||
56 | amdgpu_connector_update_scratch_regs(connector, ret); | |
57 | - pm_runtime_mark_last_busy(connector->dev->dev); | |
58 | - pm_runtime_put_autosuspend(connector->dev->dev); | |
59 | + | |
60 | + if (!drm_kms_helper_is_poll_worker()) { | |
61 | + pm_runtime_mark_last_busy(connector->dev->dev); | |
62 | + pm_runtime_put_autosuspend(connector->dev->dev); | |
63 | + } | |
64 | + | |
65 | return ret; | |
66 | } | |
67 | ||
68 | @@ -869,9 +875,11 @@ amdgpu_connector_vga_detect(struct drm_c | |
69 | enum drm_connector_status ret = connector_status_disconnected; | |
70 | int r; | |
71 | ||
72 | - r = pm_runtime_get_sync(connector->dev->dev); | |
73 | - if (r < 0) | |
74 | - return connector_status_disconnected; | |
75 | + if (!drm_kms_helper_is_poll_worker()) { | |
76 | + r = pm_runtime_get_sync(connector->dev->dev); | |
77 | + if (r < 0) | |
78 | + return connector_status_disconnected; | |
79 | + } | |
80 | ||
81 | encoder = amdgpu_connector_best_single_encoder(connector); | |
82 | if (!encoder) | |
83 | @@ -925,8 +933,10 @@ amdgpu_connector_vga_detect(struct drm_c | |
84 | amdgpu_connector_update_scratch_regs(connector, ret); | |
85 | ||
86 | out: | |
87 | - pm_runtime_mark_last_busy(connector->dev->dev); | |
88 | - pm_runtime_put_autosuspend(connector->dev->dev); | |
89 | + if (!drm_kms_helper_is_poll_worker()) { | |
90 | + pm_runtime_mark_last_busy(connector->dev->dev); | |
91 | + pm_runtime_put_autosuspend(connector->dev->dev); | |
92 | + } | |
93 | ||
94 | return ret; | |
95 | } | |
96 | @@ -989,9 +999,11 @@ amdgpu_connector_dvi_detect(struct drm_c | |
97 | enum drm_connector_status ret = connector_status_disconnected; | |
98 | bool dret = false, broken_edid = false; | |
99 | ||
100 | - r = pm_runtime_get_sync(connector->dev->dev); | |
101 | - if (r < 0) | |
102 | - return connector_status_disconnected; | |
103 | + if (!drm_kms_helper_is_poll_worker()) { | |
104 | + r = pm_runtime_get_sync(connector->dev->dev); | |
105 | + if (r < 0) | |
106 | + return connector_status_disconnected; | |
107 | + } | |
108 | ||
109 | if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) { | |
110 | ret = connector->status; | |
111 | @@ -1116,8 +1128,10 @@ out: | |
112 | amdgpu_connector_update_scratch_regs(connector, ret); | |
113 | ||
114 | exit: | |
115 | - pm_runtime_mark_last_busy(connector->dev->dev); | |
116 | - pm_runtime_put_autosuspend(connector->dev->dev); | |
117 | + if (!drm_kms_helper_is_poll_worker()) { | |
118 | + pm_runtime_mark_last_busy(connector->dev->dev); | |
119 | + pm_runtime_put_autosuspend(connector->dev->dev); | |
120 | + } | |
121 | ||
122 | return ret; | |
123 | } | |
124 | @@ -1360,9 +1374,11 @@ amdgpu_connector_dp_detect(struct drm_co | |
125 | struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); | |
126 | int r; | |
127 | ||
128 | - r = pm_runtime_get_sync(connector->dev->dev); | |
129 | - if (r < 0) | |
130 | - return connector_status_disconnected; | |
131 | + if (!drm_kms_helper_is_poll_worker()) { | |
132 | + r = pm_runtime_get_sync(connector->dev->dev); | |
133 | + if (r < 0) | |
134 | + return connector_status_disconnected; | |
135 | + } | |
136 | ||
137 | if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) { | |
138 | ret = connector->status; | |
139 | @@ -1430,8 +1446,10 @@ amdgpu_connector_dp_detect(struct drm_co | |
140 | ||
141 | amdgpu_connector_update_scratch_regs(connector, ret); | |
142 | out: | |
143 | - pm_runtime_mark_last_busy(connector->dev->dev); | |
144 | - pm_runtime_put_autosuspend(connector->dev->dev); | |
145 | + if (!drm_kms_helper_is_poll_worker()) { | |
146 | + pm_runtime_mark_last_busy(connector->dev->dev); | |
147 | + pm_runtime_put_autosuspend(connector->dev->dev); | |
148 | + } | |
149 | ||
150 | return ret; | |
151 | } |