summaryrefslogtreecommitdiffstats
path: root/sound/hda
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2016-04-21 11:49:11 -0400
committerTakashi Iwai <tiwai@suse.de>2016-04-21 11:59:17 -0400
commit3194ed497939c6448005542e3ca4fa2386968fa0 (patch)
tree88df2e9ffc4a4ba6215de034a9cabe4c11bf2428 /sound/hda
parent67f3754b51f22b18c4820fb84062f658c30e8644 (diff)
ALSA: hda - Fix possible race on regmap bypass flip
HD-audio driver uses regmap cache bypass feature for reading a raw value without the cache. But this is racy since both the cached and the uncached reads may occur concurrently. The former is done via the normal control API access while the latter comes from the proc file read. Even though the regmap itself has the protection against the concurrent accesses, the flag set/reset is done without the protection, so it may lead to inconsistent state of bypass flag that doesn't match with the current read and occasionally result in a kernel WARNING like: WARNING: CPU: 3 PID: 2731 at drivers/base/regmap/regcache.c:499 regcache_cache_only+0x78/0x93 One way to work around such a problem is to wrap with a mutex. But in this case, the solution is simpler: for the uncached read, we just skip the regmap and directly calls its accessor. The verb execution there is protected by itself, so basically it's safe to call individually. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=116171 Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/hda')
-rw-r--r--sound/hda/hdac_device.c10
-rw-r--r--sound/hda/hdac_regmap.c40
2 files changed, 32 insertions, 18 deletions
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index d1a4d6973330..03c9872c31cf 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -299,13 +299,11 @@ EXPORT_SYMBOL_GPL(_snd_hdac_read_parm);
299int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid, 299int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
300 int parm) 300 int parm)
301{ 301{
302 int val; 302 unsigned int cmd, val;
303 303
304 if (codec->regmap) 304 cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
305 regcache_cache_bypass(codec->regmap, true); 305 if (snd_hdac_regmap_read_raw_uncached(codec, cmd, &val) < 0)
306 val = snd_hdac_read_parm(codec, nid, parm); 306 return -1;
307 if (codec->regmap)
308 regcache_cache_bypass(codec->regmap, false);
309 return val; 307 return val;
310} 308}
311EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached); 309EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached);
diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c
index bdbcd6b75ff6..87041ddd29cb 100644
--- a/sound/hda/hdac_regmap.c
+++ b/sound/hda/hdac_regmap.c
@@ -453,14 +453,30 @@ int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
453EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw); 453EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
454 454
455static int reg_raw_read(struct hdac_device *codec, unsigned int reg, 455static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
456 unsigned int *val) 456 unsigned int *val, bool uncached)
457{ 457{
458 if (!codec->regmap) 458 if (uncached || !codec->regmap)
459 return hda_reg_read(codec, reg, val); 459 return hda_reg_read(codec, reg, val);
460 else 460 else
461 return regmap_read(codec->regmap, reg, val); 461 return regmap_read(codec->regmap, reg, val);
462} 462}
463 463
464static int __snd_hdac_regmap_read_raw(struct hdac_device *codec,
465 unsigned int reg, unsigned int *val,
466 bool uncached)
467{
468 int err;
469
470 err = reg_raw_read(codec, reg, val, uncached);
471 if (err == -EAGAIN) {
472 err = snd_hdac_power_up_pm(codec);
473 if (!err)
474 err = reg_raw_read(codec, reg, val, uncached);
475 snd_hdac_power_down_pm(codec);
476 }
477 return err;
478}
479
464/** 480/**
465 * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt 481 * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt
466 * @codec: the codec object 482 * @codec: the codec object
@@ -472,19 +488,19 @@ static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
472int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg, 488int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
473 unsigned int *val) 489 unsigned int *val)
474{ 490{
475 int err; 491 return __snd_hdac_regmap_read_raw(codec, reg, val, false);
476
477 err = reg_raw_read(codec, reg, val);
478 if (err == -EAGAIN) {
479 err = snd_hdac_power_up_pm(codec);
480 if (!err)
481 err = reg_raw_read(codec, reg, val);
482 snd_hdac_power_down_pm(codec);
483 }
484 return err;
485} 492}
486EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw); 493EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw);
487 494
495/* Works like snd_hdac_regmap_read_raw(), but this doesn't read from the
496 * cache but always via hda verbs.
497 */
498int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec,
499 unsigned int reg, unsigned int *val)
500{
501 return __snd_hdac_regmap_read_raw(codec, reg, val, true);
502}
503
488/** 504/**
489 * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt 505 * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
490 * @codec: the codec object 506 * @codec: the codec object