diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-04-13 05:01:14 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-04-13 05:07:20 -0400 |
commit | c3aeda62878f09da91329693a60a1f08ec97e0b8 (patch) | |
tree | 5b07fb0e0aeddc6cfb5b4b0b5f0151a679e62c4d /sound/hda | |
parent | eacf6e0a238923dfce0626450adcb6d486072f28 (diff) |
ALSA: hda - Fix another race in runtime PM refcounting
Although some races in runtime PM refcount was fixed by the commit
[664c715573c2: ALSA: hda - Work around races of power up/down with
runtime PM], there is still a race in the following case:
CPU0: CPU1 :
runtime suspend:
codec->in_pm = 1
snd_hdac_power_up_pm():
pm_runtime_get_sync() skipped
suspend finished:
codec->in_pm = 0
snd_hdac_power_down_pm():
pm_runtime_put_*() is called!
For avoiding this situation, increment in_pm flag atomically when it's
non-zero, and decrement accordingly, to ensure that in_pm is set
consistently for the whole concurrent operations.
Also, since atomic_inc_not_zero() and atomic_dec_if_positive() are
lengthy inline functions, move snd_hdac_power_up_pm() and _down_pm()
to sound/hda/hdac_device.c as no inline functions.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/hda')
-rw-r--r-- | sound/hda/hdac_device.c | 30 |
1 files changed, 30 insertions, 0 deletions
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 92604bbcee5f..f75bf5622687 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c | |||
@@ -519,6 +519,36 @@ void snd_hdac_power_down(struct hdac_device *codec) | |||
519 | pm_runtime_put_autosuspend(dev); | 519 | pm_runtime_put_autosuspend(dev); |
520 | } | 520 | } |
521 | EXPORT_SYMBOL_GPL(snd_hdac_power_down); | 521 | EXPORT_SYMBOL_GPL(snd_hdac_power_down); |
522 | |||
523 | /** | ||
524 | * snd_hdac_power_up_pm - power up the codec | ||
525 | * @codec: the codec object | ||
526 | * | ||
527 | * This function can be called in a recursive code path like init code | ||
528 | * which may be called by PM suspend/resume again. OTOH, if a power-up | ||
529 | * call must wake up the sleeper (e.g. in a kctl callback), use | ||
530 | * snd_hdac_power_up() instead. | ||
531 | */ | ||
532 | void snd_hdac_power_up_pm(struct hdac_device *codec) | ||
533 | { | ||
534 | if (!atomic_inc_not_zero(&codec->in_pm)) | ||
535 | snd_hdac_power_up(codec); | ||
536 | } | ||
537 | EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm); | ||
538 | |||
539 | /** | ||
540 | * snd_hdac_power_down_pm - power down the codec | ||
541 | * @codec: the codec object | ||
542 | * | ||
543 | * Like snd_hdac_power_up_pm(), this function is used in a recursive | ||
544 | * code path like init code which may be called by PM suspend/resume again. | ||
545 | */ | ||
546 | void snd_hdac_power_down_pm(struct hdac_device *codec) | ||
547 | { | ||
548 | if (atomic_dec_if_positive(&codec->in_pm) < 0) | ||
549 | snd_hdac_power_down(codec); | ||
550 | } | ||
551 | EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm); | ||
522 | #endif | 552 | #endif |
523 | 553 | ||
524 | /* codec vendor labels */ | 554 | /* codec vendor labels */ |