From: Takashi Iwai tiwai@suse.de
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 | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-)
--- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -133,6 +133,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) { @@ -146,12 +162,11 @@ static int __alc_read_coefex_idx(struct 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; }
@@ -168,11 +183,9 @@ static void __alc_write_coefex_idx(struc 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) \ @@ -193,11 +206,9 @@ static void alc_update_coefex_idx(struct 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) \ @@ -230,9 +241,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); @@ -240,7 +249,7 @@ static void alc_process_coef_fw(struct h __alc_update_coefex_idx(codec, fw->nid, fw->idx, fw->mask, fw->val); } - mutex_unlock(&spec->coef_mutex); + coef_mutex_unlock(codec); }
/*