Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c3aeda62 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: hda - Fix another race in runtime PM refcounting



Although some races in runtime PM refcount was fixed by the commit
[664c7155: 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: default avatarTakashi Iwai <tiwai@suse.de>
parent eacf6e0a
Loading
Loading
Loading
Loading
+4 −28
Original line number Diff line number Diff line
@@ -139,39 +139,15 @@ static inline int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid,
#ifdef CONFIG_PM
void snd_hdac_power_up(struct hdac_device *codec);
void snd_hdac_power_down(struct hdac_device *codec);
void snd_hdac_power_up_pm(struct hdac_device *codec);
void snd_hdac_power_down_pm(struct hdac_device *codec);
#else
static inline void snd_hdac_power_up(struct hdac_device *codec) {}
static inline void snd_hdac_power_down(struct hdac_device *codec) {}
static inline void snd_hdac_power_up_pm(struct hdac_device *codec) {}
static inline void snd_hdac_power_down_pm(struct hdac_device *codec) {}
#endif

/**
 * snd_hdac_power_up_pm - power up the codec
 * @codec: the codec object
 *
 * This function can be called in a recursive code path like init code
 * which may be called by PM suspend/resume again.  OTOH, if a power-up
 * call must wake up the sleeper (e.g. in a kctl callback), use
 * snd_hdac_power_up() instead.
 */
static inline void snd_hdac_power_up_pm(struct hdac_device *codec)
{
	if (!atomic_read(&codec->in_pm))
		snd_hdac_power_up(codec);
}

/**
 * snd_hdac_power_down_pm - power down the codec
 * @codec: the codec object
 *
 * Like snd_hdac_power_up_pm(), this function is used in a recursive
 * code path like init code which may be called by PM suspend/resume again.
 */
static inline void snd_hdac_power_down_pm(struct hdac_device *codec)
{
	if (!atomic_read(&codec->in_pm))
		snd_hdac_power_down(codec);
}

/*
 * HD-audio codec base driver
 */
+30 −0
Original line number Diff line number Diff line
@@ -519,6 +519,36 @@ void snd_hdac_power_down(struct hdac_device *codec)
	pm_runtime_put_autosuspend(dev);
}
EXPORT_SYMBOL_GPL(snd_hdac_power_down);

/**
 * snd_hdac_power_up_pm - power up the codec
 * @codec: the codec object
 *
 * This function can be called in a recursive code path like init code
 * which may be called by PM suspend/resume again.  OTOH, if a power-up
 * call must wake up the sleeper (e.g. in a kctl callback), use
 * snd_hdac_power_up() instead.
 */
void snd_hdac_power_up_pm(struct hdac_device *codec)
{
	if (!atomic_inc_not_zero(&codec->in_pm))
		snd_hdac_power_up(codec);
}
EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);

/**
 * snd_hdac_power_down_pm - power down the codec
 * @codec: the codec object
 *
 * Like snd_hdac_power_up_pm(), this function is used in a recursive
 * code path like init code which may be called by PM suspend/resume again.
 */
void snd_hdac_power_down_pm(struct hdac_device *codec)
{
	if (atomic_dec_if_positive(&codec->in_pm) < 0)
		snd_hdac_power_down(codec);
}
EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
#endif

/* codec vendor labels */