Skip to content

Commit b837a9f

Browse files
committed
ALSA: hda: realtek: Fix race at concurrent COEF updates
The COEF access is done with two steps: setting the index then read or write the data. When multiple COEF accesses are performed concurrently, the index and data might be paired unexpectedly. In most cases, this isn't a big problem as the COEF setup is done at the initialization, but some dynamic changes like the mute LED may hit such a race. For avoiding the racy COEF accesses, this patch introduces a new mutex coef_mutex to alc_spec, and wrap the COEF accessing functions with it. Reported-by: Alexander Sergeyev <[email protected]> Cc: <[email protected]> Link: https://lore.kernel.org/r/[email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent 0444f82 commit b837a9f

File tree

1 file changed

+50
-11
lines changed

1 file changed

+50
-11
lines changed

sound/pci/hda/patch_realtek.c

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ struct alc_spec {
9898
unsigned int gpio_mic_led_mask;
9999
struct alc_coef_led mute_led_coef;
100100
struct alc_coef_led mic_led_coef;
101+
struct mutex coef_mutex;
101102

102103
hda_nid_t headset_mic_pin;
103104
hda_nid_t headphone_mic_pin;
@@ -137,8 +138,8 @@ struct alc_spec {
137138
* COEF access helper functions
138139
*/
139140

140-
static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
141-
unsigned int coef_idx)
141+
static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
142+
unsigned int coef_idx)
142143
{
143144
unsigned int val;
144145

@@ -147,28 +148,61 @@ static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
147148
return val;
148149
}
149150

151+
static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
152+
unsigned int coef_idx)
153+
{
154+
struct alc_spec *spec = codec->spec;
155+
unsigned int val;
156+
157+
mutex_lock(&spec->coef_mutex);
158+
val = __alc_read_coefex_idx(codec, nid, coef_idx);
159+
mutex_unlock(&spec->coef_mutex);
160+
return val;
161+
}
162+
150163
#define alc_read_coef_idx(codec, coef_idx) \
151164
alc_read_coefex_idx(codec, 0x20, coef_idx)
152165

153-
static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
154-
unsigned int coef_idx, unsigned int coef_val)
166+
static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
167+
unsigned int coef_idx, unsigned int coef_val)
155168
{
156169
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx);
157170
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val);
158171
}
159172

173+
static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
174+
unsigned int coef_idx, unsigned int coef_val)
175+
{
176+
struct alc_spec *spec = codec->spec;
177+
178+
mutex_lock(&spec->coef_mutex);
179+
__alc_write_coefex_idx(codec, nid, coef_idx, coef_val);
180+
mutex_unlock(&spec->coef_mutex);
181+
}
182+
160183
#define alc_write_coef_idx(codec, coef_idx, coef_val) \
161184
alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
162185

186+
static void __alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
187+
unsigned int coef_idx, unsigned int mask,
188+
unsigned int bits_set)
189+
{
190+
unsigned int val = __alc_read_coefex_idx(codec, nid, coef_idx);
191+
192+
if (val != -1)
193+
__alc_write_coefex_idx(codec, nid, coef_idx,
194+
(val & ~mask) | bits_set);
195+
}
196+
163197
static void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
164198
unsigned int coef_idx, unsigned int mask,
165199
unsigned int bits_set)
166200
{
167-
unsigned int val = alc_read_coefex_idx(codec, nid, coef_idx);
201+
struct alc_spec *spec = codec->spec;
168202

169-
if (val != -1)
170-
alc_write_coefex_idx(codec, nid, coef_idx,
171-
(val & ~mask) | bits_set);
203+
mutex_lock(&spec->coef_mutex);
204+
__alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set);
205+
mutex_unlock(&spec->coef_mutex);
172206
}
173207

174208
#define alc_update_coef_idx(codec, coef_idx, mask, bits_set) \
@@ -201,13 +235,17 @@ struct coef_fw {
201235
static void alc_process_coef_fw(struct hda_codec *codec,
202236
const struct coef_fw *fw)
203237
{
238+
struct alc_spec *spec = codec->spec;
239+
240+
mutex_lock(&spec->coef_mutex);
204241
for (; fw->nid; fw++) {
205242
if (fw->mask == (unsigned short)-1)
206-
alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val);
243+
__alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val);
207244
else
208-
alc_update_coefex_idx(codec, fw->nid, fw->idx,
209-
fw->mask, fw->val);
245+
__alc_update_coefex_idx(codec, fw->nid, fw->idx,
246+
fw->mask, fw->val);
210247
}
248+
mutex_unlock(&spec->coef_mutex);
211249
}
212250

213251
/*
@@ -1153,6 +1191,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
11531191
codec->spdif_status_reset = 1;
11541192
codec->forced_resume = 1;
11551193
codec->patch_ops = alc_patch_ops;
1194+
mutex_init(&spec->coef_mutex);
11561195

11571196
err = alc_codec_rename_from_preset(codec);
11581197
if (err < 0) {

0 commit comments

Comments
 (0)