Thomas Zimmermann ebf85e
From d61a5c1063515e855bedb1b81e20e50b0ac3541e Mon Sep 17 00:00:00 2001
Thomas Zimmermann ebf85e
From: Lukas Wunner <lukas@wunner.de>
Thomas Zimmermann ebf85e
Date: Sun, 11 Feb 2018 10:38:28 +0100
Thomas Zimmermann ebf85e
Subject: [PATCH] drm/nouveau: Fix deadlock on runtime suspend
Thomas Zimmermann ebf85e
Git-commit: d61a5c1063515e855bedb1b81e20e50b0ac3541e
Thomas Zimmermann ebf85e
Patch-mainline: v4.16-rc3
Thomas Zimmermann ebf85e
References: bsc#1051510
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
nouveau's ->runtime_suspend hook calls drm_kms_helper_poll_disable(),
Thomas Zimmermann ebf85e
which waits for the output poll worker to finish if it's running.
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
The output poll worker meanwhile calls pm_runtime_get_sync() in
Thomas Zimmermann ebf85e
nouveau_connector_detect() which waits for the ongoing suspend to finish,
Thomas Zimmermann ebf85e
causing a deadlock.
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
Fix by not acquiring a runtime PM ref if nouveau_connector_detect() is
Thomas Zimmermann ebf85e
called in the output poll worker's context.  This is safe because
Thomas Zimmermann ebf85e
the poll worker is only enabled while runtime active and we know that
Thomas Zimmermann ebf85e
->runtime_suspend waits for it to finish.
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
Other contexts calling nouveau_connector_detect() do require a runtime
Thomas Zimmermann ebf85e
PM ref, these comprise:
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
  status_store() drm sysfs interface
Thomas Zimmermann ebf85e
  ->fill_modes drm callback
Thomas Zimmermann ebf85e
  drm_fb_helper_probe_connector_modes()
Thomas Zimmermann ebf85e
  drm_mode_getconnector()
Thomas Zimmermann ebf85e
  nouveau_connector_hotplug()
Thomas Zimmermann ebf85e
  nouveau_display_hpd_work()
Thomas Zimmermann ebf85e
  nv17_tv_set_property()
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
Stack trace for posterity:
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
  INFO: task kworker/0:1:58 blocked for more than 120 seconds.
Thomas Zimmermann ebf85e
  Workqueue: events output_poll_execute [drm_kms_helper]
Thomas Zimmermann ebf85e
  Call Trace:
Thomas Zimmermann ebf85e
   schedule+0x28/0x80
Thomas Zimmermann ebf85e
   rpm_resume+0x107/0x6e0
Thomas Zimmermann ebf85e
   __pm_runtime_resume+0x47/0x70
Thomas Zimmermann ebf85e
   nouveau_connector_detect+0x7e/0x4a0 [nouveau]
Thomas Zimmermann ebf85e
   nouveau_connector_detect_lvds+0x132/0x180 [nouveau]
Thomas Zimmermann ebf85e
   drm_helper_probe_detect_ctx+0x85/0xd0 [drm_kms_helper]
Thomas Zimmermann ebf85e
   output_poll_execute+0x11e/0x1c0 [drm_kms_helper]
Thomas Zimmermann ebf85e
   process_one_work+0x184/0x380
Thomas Zimmermann ebf85e
   worker_thread+0x2e/0x390
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
  INFO: task kworker/0:2:252 blocked for more than 120 seconds.
Thomas Zimmermann ebf85e
  Workqueue: pm pm_runtime_work
Thomas Zimmermann ebf85e
  Call Trace:
Thomas Zimmermann ebf85e
   schedule+0x28/0x80
Thomas Zimmermann ebf85e
   schedule_timeout+0x1e3/0x370
Thomas Zimmermann ebf85e
   wait_for_completion+0x123/0x190
Thomas Zimmermann ebf85e
   flush_work+0x142/0x1c0
Thomas Zimmermann ebf85e
   nouveau_pmops_runtime_suspend+0x7e/0xd0 [nouveau]
Thomas Zimmermann ebf85e
   pci_pm_runtime_suspend+0x5c/0x180
Thomas Zimmermann ebf85e
   vga_switcheroo_runtime_suspend+0x1e/0xa0
Thomas Zimmermann ebf85e
   __rpm_callback+0xc1/0x200
Thomas Zimmermann ebf85e
   rpm_callback+0x1f/0x70
Thomas Zimmermann ebf85e
   rpm_suspend+0x13c/0x640
Thomas Zimmermann ebf85e
   pm_runtime_work+0x6e/0x90
Thomas Zimmermann ebf85e
   process_one_work+0x184/0x380
Thomas Zimmermann ebf85e
   worker_thread+0x2e/0x390
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
Bugzilla: https://bugs.archlinux.org/task/53497
Thomas Zimmermann ebf85e
Bugzilla: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=870523
Thomas Zimmermann ebf85e
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=70388#c33
Thomas Zimmermann ebf85e
Fixes: 5addcf0a5f0f ("nouveau: add runtime PM support (v0.9)")
Thomas Zimmermann ebf85e
Cc: stable@vger.kernel.org # v3.12+: 27d4ee03078a: workqueue: Allow retrieval of current task's work struct
Thomas Zimmermann ebf85e
Cc: stable@vger.kernel.org # v3.12+: 25c058ccaf2e: drm: Allow determining if current task is output poll worker
Thomas Zimmermann ebf85e
Cc: Ben Skeggs <bskeggs@redhat.com>
Thomas Zimmermann ebf85e
Cc: Dave Airlie <airlied@redhat.com>
Thomas Zimmermann ebf85e
Reviewed-by: Lyude Paul <lyude@redhat.com>
Thomas Zimmermann ebf85e
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Thomas Zimmermann ebf85e
Link: https://patchwork.freedesktop.org/patch/msgid/b7d2cbb609a80f59ccabfdf479b9d5907c603ea1.1518338789.git.lukas@wunner.de
Thomas Zimmermann ebf85e
Acked-by: Takashi Iwai <tiwai@suse.de>
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
---
Thomas Zimmermann ebf85e
 drivers/gpu/drm/nouveau/nouveau_connector.c |   18 +++++++++++++-----
Thomas Zimmermann ebf85e
 1 file changed, 13 insertions(+), 5 deletions(-)
Thomas Zimmermann ebf85e
Thomas Zimmermann ebf85e
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
Thomas Zimmermann ebf85e
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
Thomas Zimmermann ebf85e
@@ -570,9 +570,15 @@ nouveau_connector_detect(struct drm_conn
Thomas Zimmermann ebf85e
 		nv_connector->edid = NULL;
Thomas Zimmermann ebf85e
 	}
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
-	ret = pm_runtime_get_sync(connector->dev->dev);
Thomas Zimmermann ebf85e
-	if (ret < 0 && ret != -EACCES)
Thomas Zimmermann ebf85e
-		return conn_status;
Thomas Zimmermann ebf85e
+	/* Outputs are only polled while runtime active, so acquiring a
Thomas Zimmermann ebf85e
+	 * runtime PM ref here is unnecessary (and would deadlock upon
Thomas Zimmermann ebf85e
+	 * runtime suspend because it waits for polling to finish).
Thomas Zimmermann ebf85e
+	 */
Thomas Zimmermann ebf85e
+	if (!drm_kms_helper_is_poll_worker()) {
Thomas Zimmermann ebf85e
+		ret = pm_runtime_get_sync(connector->dev->dev);
Thomas Zimmermann ebf85e
+		if (ret < 0 && ret != -EACCES)
Thomas Zimmermann ebf85e
+			return conn_status;
Thomas Zimmermann ebf85e
+	}
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 	nv_encoder = nouveau_connector_ddc_detect(connector);
Thomas Zimmermann ebf85e
 	if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) {
Thomas Zimmermann ebf85e
@@ -647,8 +653,10 @@ detect_analog:
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
  out:
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
-	pm_runtime_mark_last_busy(connector->dev->dev);
Thomas Zimmermann ebf85e
-	pm_runtime_put_autosuspend(connector->dev->dev);
Thomas Zimmermann ebf85e
+	if (!drm_kms_helper_is_poll_worker()) {
Thomas Zimmermann ebf85e
+		pm_runtime_mark_last_busy(connector->dev->dev);
Thomas Zimmermann ebf85e
+		pm_runtime_put_autosuspend(connector->dev->dev);
Thomas Zimmermann ebf85e
+	}
Thomas Zimmermann ebf85e
 
Thomas Zimmermann ebf85e
 	return conn_status;
Thomas Zimmermann ebf85e
 }