]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
ALSA: hda/realtek: Fix deadlock by COEF mutex
authorTakashi Iwai <tiwai@suse.de>
Mon, 14 Feb 2022 13:04:10 +0000 (14:04 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 23 Feb 2022 11:01:03 +0000 (12:01 +0100)
commit 2a845837e3d0ddaed493b4c5c4643d7f0542804d upstream.

The recently introduced coef_mutex for Realtek codec seems causing a
deadlock when the relevant code is invoked from the power-off state;
then the HD-audio core tries to power-up internally, and this kicks
off the codec runtime PM code that tries to take the same coef_mutex.

In order to avoid the deadlock, do the temporary power up/down around
the coef_mutex acquisition and release.  This assures that the
power-up sequence runs before the mutex, hence no re-entrance will
happen.

Fixes: b837a9f5ab3b ("ALSA: hda: realtek: Fix race at concurrent COEF updates")
Reported-and-tested-by: Julian Wollrath <jwollrath@web.de>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20220214132838.4db10fca@schienar
Link: https://lore.kernel.org/r/20220214130410.21230-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
sound/pci/hda/patch_realtek.c

index 52b9023fc995554032700830b20b615c94705425..ed0cfcb05ef0d4ee0243a1ed4d47ea17a494313f 100644 (file)
@@ -134,6 +134,22 @@ struct alc_spec {
  * COEF access helper functions
  */
 
+static void coef_mutex_lock(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       snd_hda_power_up_pm(codec);
+       mutex_lock(&spec->coef_mutex);
+}
+
+static void coef_mutex_unlock(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       mutex_unlock(&spec->coef_mutex);
+       snd_hda_power_down_pm(codec);
+}
+
 static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
                                 unsigned int coef_idx)
 {
@@ -147,12 +163,11 @@ static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
 static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
                               unsigned int coef_idx)
 {
-       struct alc_spec *spec = codec->spec;
        unsigned int val;
 
-       mutex_lock(&spec->coef_mutex);
+       coef_mutex_lock(codec);
        val = __alc_read_coefex_idx(codec, nid, coef_idx);
-       mutex_unlock(&spec->coef_mutex);
+       coef_mutex_unlock(codec);
        return val;
 }
 
@@ -169,11 +184,9 @@ static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
 static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
                                 unsigned int coef_idx, unsigned int coef_val)
 {
-       struct alc_spec *spec = codec->spec;
-
-       mutex_lock(&spec->coef_mutex);
+       coef_mutex_lock(codec);
        __alc_write_coefex_idx(codec, nid, coef_idx, coef_val);
-       mutex_unlock(&spec->coef_mutex);
+       coef_mutex_unlock(codec);
 }
 
 #define alc_write_coef_idx(codec, coef_idx, coef_val) \
@@ -194,11 +207,9 @@ static void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
                                  unsigned int coef_idx, unsigned int mask,
                                  unsigned int bits_set)
 {
-       struct alc_spec *spec = codec->spec;
-
-       mutex_lock(&spec->coef_mutex);
+       coef_mutex_lock(codec);
        __alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set);
-       mutex_unlock(&spec->coef_mutex);
+       coef_mutex_unlock(codec);
 }
 
 #define alc_update_coef_idx(codec, coef_idx, mask, bits_set)   \
@@ -231,9 +242,7 @@ struct coef_fw {
 static void alc_process_coef_fw(struct hda_codec *codec,
                                const struct coef_fw *fw)
 {
-       struct alc_spec *spec = codec->spec;
-
-       mutex_lock(&spec->coef_mutex);
+       coef_mutex_lock(codec);
        for (; fw->nid; fw++) {
                if (fw->mask == (unsigned short)-1)
                        __alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val);
@@ -241,7 +250,7 @@ static void alc_process_coef_fw(struct hda_codec *codec,
                        __alc_update_coefex_idx(codec, fw->nid, fw->idx,
                                                fw->mask, fw->val);
        }
-       mutex_unlock(&spec->coef_mutex);
+       coef_mutex_unlock(codec);
 }
 
 /*