Commit 2a845837 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: hda/realtek: Fix deadlock by COEF mutex



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: b837a9f5 ("ALSA: hda: realtek: Fix race at concurrent COEF updates")
Reported-and-tested-by: default avatarJulian 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: default avatarTakashi Iwai <tiwai@suse.de>
parent 9a5adeb2
Loading
Loading
Loading
Loading
+24 −15
Original line number Diff line number Diff line
@@ -138,6 +138,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)
{
@@ -151,12 +167,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;
}

@@ -173,11 +188,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) \
@@ -198,11 +211,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)	\
@@ -235,9 +246,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);
@@ -245,7 +254,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);
}

/*