Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: hda: Manage concurrent reg access more properly

In the commit 8e85def5723e ("ALSA: hda: enable regmap internal
locking"), we re-enabled the regmap lock due to the reported
regression that showed the possible concurrent accesses. It was a
temporary workaround, and there are still a few opened races even
after the revert. In this patch, we cover those still opened windows
with a proper mutex lock and disable the regmap internal lock again.

First off, the patch introduces a new snd_hdac_device.regmap_lock
mutex that is applied for each snd_hdac_regmap_*() call, including
read, write and update helpers. The mutex is applied carefully so
that it won't block the self-power-up procedure in the helper
function. Also, this assures the protection for the accesses without
regmap, too.

The snd_hdac_regmap_update_raw() is refactored to use the standard
regmap_update_bits_check() function instead of the open-code. The
non-regmap case is still open-coded but it's an easy part. The all
read and write operations are in the single mutex protection, so it's
now race-free.

In addition, a couple of new helper functions are added:
snd_hdac_regmap_update_raw_once() and snd_hdac_regmap_sync(). Both
are called from HD-audio legacy driver. The former is to initialize
the given verb bits but only once when it's not initialized yet. Due
to this condition, the function invokes regcache_cache_only(), and
it's now performed inside the regmap_lock (formerly it was racy) too.
The latter function is for simply invoking regcache_sync() inside the
regmap_lock, which is called from the codec resume call path.
Along with that, the HD-audio codec driver code is slightly modified /
simplified to adapt those new functions.

And finally, snd_hdac_regmap_read_raw(), *_write_raw(), etc are
rewritten with the helper macro. It's just for simplification because
the code logic is identical among all those functions.

Tested-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/20200109090104.26073-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>

+135 -54
+3
include/sound/hda_regmap.h
··· 24 24 unsigned int val); 25 25 int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg, 26 26 unsigned int mask, unsigned int val); 27 + int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsigned int reg, 28 + unsigned int mask, unsigned int val); 29 + void snd_hdac_regmap_sync(struct hdac_device *codec); 27 30 28 31 /** 29 32 * snd_hdac_regmap_encode_verb - encode the verb to a pseudo register
+1
include/sound/hdaudio.h
··· 86 86 87 87 /* regmap */ 88 88 struct regmap *regmap; 89 + struct mutex regmap_lock; 89 90 struct snd_array vendor_verbs; 90 91 bool lazy_cache:1; /* don't wake up for writes */ 91 92 bool caps_overwriting:1; /* caps overwrite being in process */
+1
sound/hda/hdac_device.c
··· 57 57 codec->addr = addr; 58 58 codec->type = HDA_DEV_CORE; 59 59 mutex_init(&codec->widget_lock); 60 + mutex_init(&codec->regmap_lock); 60 61 pm_runtime_set_active(&codec->dev); 61 62 pm_runtime_get_noresume(&codec->dev); 62 63 atomic_set(&codec->in_pm, 0);
+107 -35
sound/hda/hdac_regmap.c
··· 363 363 .reg_write = hda_reg_write, 364 364 .use_single_read = true, 365 365 .use_single_write = true, 366 + .disable_locking = true, 366 367 }; 367 368 368 369 /** ··· 426 425 static int reg_raw_write(struct hdac_device *codec, unsigned int reg, 427 426 unsigned int val) 428 427 { 428 + int err; 429 + 430 + mutex_lock(&codec->regmap_lock); 429 431 if (!codec->regmap) 430 - return hda_reg_write(codec, reg, val); 432 + err = hda_reg_write(codec, reg, val); 431 433 else 432 - return regmap_write(codec->regmap, reg, val); 434 + err = regmap_write(codec->regmap, reg, val); 435 + mutex_unlock(&codec->regmap_lock); 436 + return err; 433 437 } 438 + 439 + /* a helper macro to call @func_call; retry with power-up if failed */ 440 + #define CALL_RAW_FUNC(codec, func_call) \ 441 + ({ \ 442 + int _err = func_call; \ 443 + if (_err == -EAGAIN) { \ 444 + _err = snd_hdac_power_up_pm(codec); \ 445 + if (_err >= 0) \ 446 + _err = func_call; \ 447 + snd_hdac_power_down_pm(codec); \ 448 + } \ 449 + _err;}) 434 450 435 451 /** 436 452 * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt ··· 460 442 int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg, 461 443 unsigned int val) 462 444 { 463 - int err; 464 - 465 - err = reg_raw_write(codec, reg, val); 466 - if (err == -EAGAIN) { 467 - err = snd_hdac_power_up_pm(codec); 468 - if (err >= 0) 469 - err = reg_raw_write(codec, reg, val); 470 - snd_hdac_power_down_pm(codec); 471 - } 472 - return err; 445 + return CALL_RAW_FUNC(codec, reg_raw_write(codec, reg, val)); 473 446 } 474 447 EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw); 475 448 476 449 static int reg_raw_read(struct hdac_device *codec, unsigned int reg, 477 450 unsigned int *val, bool uncached) 478 451 { 452 + int err; 453 + 454 + mutex_lock(&codec->regmap_lock); 479 455 if (uncached || !codec->regmap) 480 - return hda_reg_read(codec, reg, val); 456 + err = hda_reg_read(codec, reg, val); 481 457 else 482 - return regmap_read(codec->regmap, reg, val); 458 + err = regmap_read(codec->regmap, reg, val); 459 + mutex_unlock(&codec->regmap_lock); 460 + return err; 483 461 } 484 462 485 463 static int __snd_hdac_regmap_read_raw(struct hdac_device *codec, 486 464 unsigned int reg, unsigned int *val, 487 465 bool uncached) 488 466 { 489 - int err; 490 - 491 - err = reg_raw_read(codec, reg, val, uncached); 492 - if (err == -EAGAIN) { 493 - err = snd_hdac_power_up_pm(codec); 494 - if (err >= 0) 495 - err = reg_raw_read(codec, reg, val, uncached); 496 - snd_hdac_power_down_pm(codec); 497 - } 498 - return err; 467 + return CALL_RAW_FUNC(codec, reg_raw_read(codec, reg, val, uncached)); 499 468 } 500 469 501 470 /** ··· 509 504 return __snd_hdac_regmap_read_raw(codec, reg, val, true); 510 505 } 511 506 507 + static int reg_raw_update(struct hdac_device *codec, unsigned int reg, 508 + unsigned int mask, unsigned int val) 509 + { 510 + unsigned int orig; 511 + bool change; 512 + int err; 513 + 514 + mutex_lock(&codec->regmap_lock); 515 + if (codec->regmap) { 516 + err = regmap_update_bits_check(codec->regmap, reg, mask, val, 517 + &change); 518 + if (!err) 519 + err = change ? 1 : 0; 520 + } else { 521 + err = hda_reg_read(codec, reg, &orig); 522 + if (!err) { 523 + val &= mask; 524 + val |= orig & ~mask; 525 + if (val != orig) { 526 + err = hda_reg_write(codec, reg, val); 527 + if (!err) 528 + err = 1; 529 + } 530 + } 531 + } 532 + mutex_unlock(&codec->regmap_lock); 533 + return err; 534 + } 535 + 512 536 /** 513 537 * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt 514 538 * @codec: the codec object ··· 550 516 int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg, 551 517 unsigned int mask, unsigned int val) 552 518 { 519 + return CALL_RAW_FUNC(codec, reg_raw_update(codec, reg, mask, val)); 520 + } 521 + EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw); 522 + 523 + static int reg_raw_update_once(struct hdac_device *codec, unsigned int reg, 524 + unsigned int mask, unsigned int val) 525 + { 553 526 unsigned int orig; 554 527 int err; 555 528 556 - val &= mask; 557 - err = snd_hdac_regmap_read_raw(codec, reg, &orig); 529 + if (!codec->regmap) 530 + return reg_raw_update(codec, reg, mask, val); 531 + 532 + mutex_lock(&codec->regmap_lock); 533 + regcache_cache_only(codec->regmap, true); 534 + err = regmap_read(codec->regmap, reg, &orig); 535 + regcache_cache_only(codec->regmap, false); 558 536 if (err < 0) 559 - return err; 560 - val |= orig & ~mask; 561 - if (val == orig) 562 - return 0; 563 - err = snd_hdac_regmap_write_raw(codec, reg, val); 564 - if (err < 0) 565 - return err; 566 - return 1; 537 + err = regmap_update_bits(codec->regmap, reg, mask, val); 538 + mutex_unlock(&codec->regmap_lock); 539 + return err; 567 540 } 568 - EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw); 541 + 542 + /** 543 + * snd_hdac_regmap_update_raw_once - initialize the register value only once 544 + * @codec: the codec object 545 + * @reg: pseudo register 546 + * @mask: bit mask to update 547 + * @val: value to update 548 + * 549 + * Performs the update of the register bits only once when the register 550 + * hasn't been initialized yet. Used in HD-audio legacy driver. 551 + * Returns zero if successful or a negative error code 552 + */ 553 + int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsigned int reg, 554 + unsigned int mask, unsigned int val) 555 + { 556 + return CALL_RAW_FUNC(codec, reg_raw_update_once(codec, reg, mask, val)); 557 + } 558 + EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw_once); 559 + 560 + /** 561 + * snd_hdac_regmap_sync - sync out the cached values for PM resume 562 + * @codec: the codec object 563 + */ 564 + void snd_hdac_regmap_sync(struct hdac_device *codec) 565 + { 566 + if (codec->regmap) { 567 + mutex_lock(&codec->regmap_lock); 568 + regcache_sync(codec->regmap); 569 + mutex_unlock(&codec->regmap_lock); 570 + } 571 + } 572 + EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync);
+16 -14
sound/pci/hda/hda_codec.c
··· 1267 1267 } 1268 1268 EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); 1269 1269 1270 + static unsigned int encode_amp(struct hda_codec *codec, hda_nid_t nid, 1271 + int ch, int dir, int idx) 1272 + { 1273 + unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx); 1274 + 1275 + /* enable fake mute if no h/w mute but min=mute */ 1276 + if ((query_amp_caps(codec, nid, dir) & 1277 + (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE) 1278 + cmd |= AC_AMP_FAKE_MUTE; 1279 + return cmd; 1280 + } 1281 + 1270 1282 /** 1271 1283 * snd_hda_codec_amp_update - update the AMP mono value 1272 1284 * @codec: HD-audio codec ··· 1294 1282 int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, 1295 1283 int ch, int dir, int idx, int mask, int val) 1296 1284 { 1297 - unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx); 1285 + unsigned int cmd = encode_amp(codec, nid, ch, dir, idx); 1298 1286 1299 - /* enable fake mute if no h/w mute but min=mute */ 1300 - if ((query_amp_caps(codec, nid, dir) & 1301 - (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE) 1302 - cmd |= AC_AMP_FAKE_MUTE; 1303 1287 return snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val); 1304 1288 } 1305 1289 EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update); ··· 1343 1335 int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, 1344 1336 int dir, int idx, int mask, int val) 1345 1337 { 1346 - int orig; 1338 + unsigned int cmd = encode_amp(codec, nid, ch, dir, idx); 1347 1339 1348 1340 if (!codec->core.regmap) 1349 1341 return -EINVAL; 1350 - regcache_cache_only(codec->core.regmap, true); 1351 - orig = snd_hda_codec_amp_read(codec, nid, ch, dir, idx); 1352 - regcache_cache_only(codec->core.regmap, false); 1353 - if (orig >= 0) 1354 - return 0; 1355 - return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val); 1342 + return snd_hdac_regmap_update_raw_once(&codec->core, cmd, mask, val); 1356 1343 } 1357 1344 EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init); 1358 1345 ··· 2908 2905 else { 2909 2906 if (codec->patch_ops.init) 2910 2907 codec->patch_ops.init(codec); 2911 - if (codec->core.regmap) 2912 - regcache_sync(codec->core.regmap); 2908 + snd_hda_regmap_sync(codec); 2913 2909 } 2914 2910 2915 2911 if (codec->jackpoll_interval)
+1 -1
sound/pci/hda/hda_generic.c
··· 6027 6027 /* call init functions of standard auto-mute helpers */ 6028 6028 update_automute_all(codec); 6029 6029 6030 - regcache_sync(codec->core.regmap); 6030 + snd_hda_regmap_sync(codec); 6031 6031 6032 6032 if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) 6033 6033 snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+2
sound/pci/hda/hda_local.h
··· 138 138 void snd_hda_codec_register(struct hda_codec *codec); 139 139 void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); 140 140 141 + #define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core) 142 + 141 143 enum { 142 144 HDA_VMUTE_OFF, 143 145 HDA_VMUTE_ON,
+1 -1
sound/pci/hda/patch_hdmi.c
··· 2404 2404 int pin_idx; 2405 2405 2406 2406 codec->patch_ops.init(codec); 2407 - regcache_sync(codec->core.regmap); 2407 + snd_hda_regmap_sync(codec); 2408 2408 2409 2409 for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { 2410 2410 struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+2 -2
sound/pci/hda/patch_realtek.c
··· 907 907 if (!spec->no_depop_delay) 908 908 msleep(150); /* to avoid pop noise */ 909 909 codec->patch_ops.init(codec); 910 - regcache_sync(codec->core.regmap); 910 + snd_hda_regmap_sync(codec); 911 911 hda_call_check_power_status(codec, 0x01); 912 912 return 0; 913 913 } ··· 3638 3638 msleep(200); 3639 3639 } 3640 3640 3641 - regcache_sync(codec->core.regmap); 3641 + snd_hda_regmap_sync(codec); 3642 3642 hda_call_check_power_status(codec, 0x01); 3643 3643 3644 3644 /* on some machine, the BIOS will clear the codec gpio data when enter
+1 -1
sound/pci/hda/patch_via.c
··· 396 396 /* some delay here to make jack detection working (bko#98921) */ 397 397 msleep(10); 398 398 codec->patch_ops.init(codec); 399 - regcache_sync(codec->core.regmap); 399 + snd_hda_regmap_sync(codec); 400 400 return 0; 401 401 } 402 402 #endif