ALSA: hda/realtek: Fix deadlock by COEF mutex
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>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
b6a5e8f45f
commit
63b1602c2f
@@ -134,6 +134,22 @@ struct alc_spec {
|
|||||||
* COEF access helper functions
|
* 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,
|
static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
|
||||||
unsigned int coef_idx)
|
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,
|
static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
|
||||||
unsigned int coef_idx)
|
unsigned int coef_idx)
|
||||||
{
|
{
|
||||||
struct alc_spec *spec = codec->spec;
|
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
|
|
||||||
mutex_lock(&spec->coef_mutex);
|
coef_mutex_lock(codec);
|
||||||
val = __alc_read_coefex_idx(codec, nid, coef_idx);
|
val = __alc_read_coefex_idx(codec, nid, coef_idx);
|
||||||
mutex_unlock(&spec->coef_mutex);
|
coef_mutex_unlock(codec);
|
||||||
return val;
|
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,
|
static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
|
||||||
unsigned int coef_idx, unsigned int coef_val)
|
unsigned int coef_idx, unsigned int coef_val)
|
||||||
{
|
{
|
||||||
struct alc_spec *spec = codec->spec;
|
coef_mutex_lock(codec);
|
||||||
|
|
||||||
mutex_lock(&spec->coef_mutex);
|
|
||||||
__alc_write_coefex_idx(codec, nid, coef_idx, coef_val);
|
__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) \
|
#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 coef_idx, unsigned int mask,
|
||||||
unsigned int bits_set)
|
unsigned int bits_set)
|
||||||
{
|
{
|
||||||
struct alc_spec *spec = codec->spec;
|
coef_mutex_lock(codec);
|
||||||
|
|
||||||
mutex_lock(&spec->coef_mutex);
|
|
||||||
__alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set);
|
__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) \
|
#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,
|
static void alc_process_coef_fw(struct hda_codec *codec,
|
||||||
const struct coef_fw *fw)
|
const struct coef_fw *fw)
|
||||||
{
|
{
|
||||||
struct alc_spec *spec = codec->spec;
|
coef_mutex_lock(codec);
|
||||||
|
|
||||||
mutex_lock(&spec->coef_mutex);
|
|
||||||
for (; fw->nid; fw++) {
|
for (; fw->nid; fw++) {
|
||||||
if (fw->mask == (unsigned short)-1)
|
if (fw->mask == (unsigned short)-1)
|
||||||
__alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val);
|
__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,
|
__alc_update_coefex_idx(codec, fw->nid, fw->idx,
|
||||||
fw->mask, fw->val);
|
fw->mask, fw->val);
|
||||||
}
|
}
|
||||||
mutex_unlock(&spec->coef_mutex);
|
coef_mutex_unlock(codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user