Blob Blame History Raw
From 4bdc42094d9c4af75f90ee49b9009ea8dfe41444 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Wed, 10 Apr 2019 13:03:22 +0200
Subject: [PATCH] ALSA: hda: Fix racy display power access
Git-commit: 4bdc42094d9c4af75f90ee49b9009ea8dfe41444
Patch-mainline: v5.2-rc1
References: bsc#1156928

[ backport note: SLE15-SP1 & co still don't have bus->lock, and we can't
  add this new field due to kABI compatibility.
  In this patch, a global lock is used instead, which should be good enough
  -- tiwai ]

snd_hdac_display_power() doesn't handle the concurrent calls carefully
enough, and it may lead to the doubly get_power or put_power calls,
when a runtime PM and an async work get called in racy way.

This patch addresses it by reusing the bus->lock mutex that has been
used for protecting the link state change in ext bus code, so that it
can protect against racy display state changes.  The initialization of
bus->lock was moved from snd_hdac_ext_bus_init() to
snd_hdac_bus_init() as well accordingly.

Testcase: igt/i915_pm_rpm/module-reload #glk-dsi
Reported-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Imre Deak <imre.deak@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: https://patchwork.freedesktop.org/patch/msgid/s5h8swiunph.wl-tiwai@suse.de

---
 sound/hda/hdac_component.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

--- a/sound/hda/hdac_component.c
+++ b/sound/hda/hdac_component.c
@@ -10,6 +10,8 @@
 #include <sound/hda_component.h>
 #include <sound/hda_register.h>
 
+static DEFINE_MUTEX(hdac_bus_lock);
+
 static void hdac_acomp_release(struct device *dev, void *res)
 {
 }
@@ -71,13 +73,15 @@ int snd_hdac_display_power(struct hdac_b
 
 	dev_dbg(bus->dev, "display power %s\n",
 		enable ? "enable" : "disable");
+
+	mutex_lock(&hdac_bus_lock);
 	if (enable)
 		set_bit(idx, &bus->display_power_status);
 	else
 		clear_bit(idx, &bus->display_power_status);
 
 	if (!acomp || !acomp->ops)
-		return 0;
+		goto unlock;
 
 	if (bus->display_power_status) {
 		if (!bus->display_power_active) {
@@ -95,6 +99,8 @@ int snd_hdac_display_power(struct hdac_b
 		}
 	}
 
+ unlock:
+	mutex_unlock(&hdac_bus_lock);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_display_power);